From 193ee11fe1f7ce00ecb9c65dd95e4d0f779400e5 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 2 Oct 2022 00:09:40 +0000 Subject: [PATCH] General internal cleanups in the oo1 API. FossilOrigin-Name: f9db664f756f3707afcb5dce87f6d946625848f27ea84337af68de72d4ad6c6b --- ext/wasm/api/sqlite3-api-oo1.js | 109 +++++++++++++-------------- ext/wasm/api/sqlite3-api-prologue.js | 23 +++++- ext/wasm/testing1.js | 2 +- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 84 insertions(+), 68 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 19e9e61f08..627af2e8a5 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -16,6 +16,7 @@ */ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss = (...args)=>{throw new Error(args.join(' '))}; + const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)}; const capi = sqlite3.capi, util = capi.util; /* What follows is colloquially known as "OO API #1". It is a @@ -53,28 +54,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const getOwnOption = (opts, p, dflt)=> opts.hasOwnProperty(p) ? opts[p] : dflt; - /** - An Error subclass specifically for reporting DB-level errors and - enabling clients to unambiguously identify such exceptions. - */ - class SQLite3Error extends Error { - /** - Constructs this object with a message equal to all arguments - concatenated with a space between each one. - */ - constructor(...args){ - super(args.join(' ')); - this.name = 'SQLite3Error'; - } - }; - 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( + toss3( "sqlite result code",sqliteResultCode+":", (dbPtr ? capi.sqlite3_errmsg(dbPtr) @@ -183,13 +167,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ If passed an object, any additional properties it has are copied as-is into the new object. */ - dbCtorHelper.normalizeArgs = function(filename,flags = 'c',vfs = null){ + dbCtorHelper.normalizeArgs = function(filename=':memory:',flags = 'c',vfs = null){ const arg = {}; if(1===arguments.length && 'object'===typeof arguments[0]){ const x = arguments[0]; Object.keys(x).forEach((k)=>arg[k] = x[k]); if(undefined===arg.flags) arg.flags = 'c'; if(undefined===arg.vfs) arg.vfs = null; + if(undefined===arg.filename) arg.filename = ':memory:'; }else{ arg.filename = filename; arg.flags = flags; @@ -232,7 +217,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 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 + or none at all, to use the default. If passed a value, it must be the string name of a VFS The constructor optionally (and preferably) takes its arguments @@ -554,15 +539,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ prepare: function(sql){ affirmDbOpen(this); - if(Array.isArray(sql)) sql = sql.join(''); - const stack = capi.wasm.scopedAllocPush(); + const stack = capi.wasm.pstack.pointer; let ppStmt, pStmt; try{ - ppStmt = capi.wasm.scopedAllocPtr()/* output (sqlite3_stmt**) arg */; + ppStmt = capi.wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */; DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); pStmt = capi.wasm.getPtrValue(ppStmt); } - finally {capi.wasm.scopedAllocPop(stack)} + finally { + capi.wasm.pstack.restore(stack); + } if(!pStmt) toss3("Cannot prepare empty SQL."); const stmt = new Stmt(this, pStmt, BindTypes); __stmtMap.get(this)[pStmt] = stmt; @@ -977,7 +963,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ SQLITE_{typename} constants. Passing the undefined value is the same as not passing a value. - Throws on error (e.g. malformedSQL). + Throws on error (e.g. malformed SQL). */ selectValue: function(sql,bind,asType){ let stmt, rc; @@ -1157,7 +1143,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } } }; - } + }/* static init */ affirmSupportedBindType(val); ndx = affirmParamIndex(stmt,ndx); let rc = 0; @@ -1171,15 +1157,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ case BindTypes.number: { let m; if(util.isInt32(val)) m = capi.sqlite3_bind_int; - else if(capi.wasm.bigIntEnabled && ('bigint'===typeof val)){ + else if('bigint'===typeof val){ if(valf._maxInt){ - toss3("BigInt value is out of range for int64: "+val); + toss3("BigInt value is out of range for storing as int64: "+val); + }else if(capi.wasm.bigIntEnabled){ + m = capi.sqlite3_bind_int64; + }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){ + val = Number(val); + m = capi.sqlite3_bind_double; + }else{ + toss3("BigInt value is out of range for storing as double: "+val); + } + }else{ // !int32, !bigint + val = Number(val); + if(capi.wasm.bigIntEnabled && Number.isInteger(val)){ + m = capi.sqlite3_bind_int64; + }else{ + m = capi.sqlite3_bind_double; } - m = capi.sqlite3_bind_int64; - }else if(Number.isInteger(val)){ - m = capi.sqlite3_bind_int64; - }else{ - m = capi.sqlite3_bind_double; } rc = m(stmt.pointer, ndx, val); break; @@ -1283,31 +1278,32 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - null is bound as NULL. - undefined as a standalone value is a no-op intended to - simplify certain client-side use cases: passing undefined - as a value to this function will not actually bind - anything and this function will skip confirmation that - binding is even legal. (Those semantics simplify certain - client-side uses.) Conversely, a value of undefined as an - array or object property when binding an array/object - (see below) is treated the same as null. + simplify certain client-side use cases: passing undefined as + a value to this function will not actually bind anything and + this function will skip confirmation that binding is even + legal. (Those semantics simplify certain client-side uses.) + Conversely, a value of undefined as an array or object + property when binding an array/object (see below) is treated + the same as null. - - Numbers are bound as either doubles or integers: doubles - if they are larger than 32 bits, else double or int32, - depending on whether they have a fractional part. (It is, - as of this writing, illegal to call (from JS) a WASM - function which either takes or returns an int64.) - Booleans are bound as integer 0 or 1. It is not expected - the distinction of binding doubles which have no - fractional parts is integers is significant for the - majority of clients due to sqlite3's data typing - model. If capi.wasm.bigIntEnabled is true then this - routine will bind BigInt values as 64-bit integers. + - Numbers are bound as either doubles or integers: doubles if + they are larger than 32 bits, else double or int32, depending + on whether they have a fractional part. Booleans are bound as + integer 0 or 1. It is not expected the distinction of binding + doubles which have no fractional parts is integers is + significant for the majority of clients due to sqlite3's data + typing model. If [BigInt] support is enabled then this + routine will bind BigInt values as 64-bit integers if they'll + fit in 64 bits. If that support disabled, it will store the + BigInt as an int32 or a double if it can do so without loss + of precision. If the BigInt is _too BigInt_ then it will + throw. - Strings are bound as strings (use bindAsBlob() to force - blob binding). + blob binding). - Uint8Array and Int8Array instances are bound as blobs. - (TODO: binding the other TypedArray types.) + (TODO: binding the other TypedArray types.) If passed an array, each element of the array is bound at the parameter index equal to the array index plus 1 @@ -1458,7 +1454,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ This is intended to simplify use cases such as: ``` - aDb.prepare("insert in foo(a) values(?)").bind(123).stepFinalize(); + aDb.prepare("insert into foo(a) values(?)").bind(123).stepFinalize(); ``` */ stepFinalize: function(){ @@ -1595,7 +1591,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }, // Design note: the only reason most of these getters have a 'get' // prefix is for consistency with getVALUE_TYPE(). The latter - // arguablly really need that prefix for API readability and the + // arguably really need that prefix for API readability and the // rest arguably don't, but consistency is a powerful thing. /** Returns the result column name of the given index, or @@ -1616,9 +1612,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ cannot have result columns. This object's columnCount member holds the number of columns. */ - getColumnNames: function(tgt){ + getColumnNames: function(tgt=[]){ affirmColIndex(affirmStmtOpen(this),0); - if(!tgt) tgt = []; for(let i = 0; i < this.columnCount; ++i){ tgt.push(capi.sqlite3_column_name(this.pointer, i)); } diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index b00eeb8633..97376be6c8 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -785,7 +785,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( be released using restore(). This method always adjusts the given value to be a multiple - of 8 in order to keep alignment guarantees. + of 8 bytes because failing to do so can lead to incorrect + results when reading and writing 64-bit values from/to the WASM + heap. */ alloc: capi.wasm.exports.sqlite3_wasm_pstack_alloc }); @@ -809,6 +811,24 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( get: capi.wasm.exports.sqlite3_wasm_pstack_remaining }); + /** + An Error subclass specifically for reporting DB-level errors and + enabling clients to unambiguously identify such exceptions. + The C-level APIs never throw, but some of the higher-level + C-style APIs do and the object-oriented APIs use exceptions + exclusively to report errors. + */ + class SQLite3Error extends Error { + /** + Constructs this object with a message equal to all arguments + concatenated with a space between each one. + */ + constructor(...args){ + super(args.join(' ')); + this.name = 'SQLite3Error'; + } + }; + /** State for sqlite3_wasmfs_opfs_dir(). */ let __persistentDir = undefined; @@ -1059,6 +1079,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /* The remainder of the API will be set up in later steps. */ const sqlite3 = { WasmAllocError: WasmAllocError, + SQLite3Error: SQLite3Error, capi, config, /** diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index ffe63c537c..dd61ab2814 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -354,7 +354,7 @@ T.assert(g64(pMin) === minMaxI64[0]). assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); - const rxRange = /out of range for int64/; + const rxRange = /out of range for storing as int64/; T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))}, rxRange). mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))}, diff --git a/manifest b/manifest index 8e269668d8..44d3de74a9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sfiddle\sdb\sexport\sbreakage\scaused\sby\sa\spost-testing\sAPI\schange\smade\sin\s[1fa019c88dea]. -D 2022-10-01T19:21:52.215 +C General\sinternal\scleanups\sin\sthe\soo1\sAPI. +D 2022-10-02T00:09:40.160 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -486,9 +486,9 @@ F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1 F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99 F ext/wasm/api/sqlite3-api-glue.js b15a51b88aaa472d36bf82d5123dbfdafe8ddf6ca75fba934510e4a20bbe4adb -F ext/wasm/api/sqlite3-api-oo1.js 9caed0757a5e039ed92467e827fd3ca347fa08f19fe086fcbdd14a4ebe9c2f01 +F ext/wasm/api/sqlite3-api-oo1.js 7667d320f6b9fb5252050a2f9c0b1769e11b84dbc0763b999baf65b451b14369 F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 -F ext/wasm/api/sqlite3-api-prologue.js 8692571345efc0f6844092e5ffa86cbd72af0fa23696599c04cc28d64c5d1d11 +F ext/wasm/api/sqlite3-api-prologue.js a93bd69969eb8b8f9c4cb34e5d86dcbbe5adbeeea39c1cce57194256c5f28434 F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c d72aecf0e50a4403402095ef4e8d6a814fdc2256589944c1dc974c70d2f65b7e @@ -530,7 +530,7 @@ F ext/wasm/test-opfs-vfs.js a59ff9210b17d46b0c6fbf6a0ba60143c033327865f2e556e14f F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js 419e029ee4ae3cf98dd4c20aebdb111afe743b54ed8856e5f59775c3241fd21f +F ext/wasm/testing1.js 51ef1ced0669f804787ce96f19dcf64367550a7923a998514be56076326988d7 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494 F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f @@ -2029,8 +2029,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 c8a173cf16d2567bf9b13f52904077a2e7209776c76613c7bff59cd66e24bf11 -R d4bf3165ea62de5a9f288d66d4a2cd95 +P e73cc44ec36c7585ebb914bdeed5b39480fca7c7a8d3c4426bfe769c87b98d17 +R 4b53a976c2062b810a77639258e8be02 U stephan -Z a03f054bd345e55a260c4a080b874912 +Z f0b55570286779c7113ac4ef57233011 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6eb12af986..1d593beead 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e73cc44ec36c7585ebb914bdeed5b39480fca7c7a8d3c4426bfe769c87b98d17 \ No newline at end of file +f9db664f756f3707afcb5dce87f6d946625848f27ea84337af68de72d4ad6c6b \ No newline at end of file