From: stephan Date: Sat, 25 Jun 2022 03:53:43 +0000 (+0000) Subject: wasm: corrected the propagation of text/blob values via UDFs. DB.exec()'s sql may... X-Git-Tag: version-3.40.0~246^2~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=766ba7917e0f01929fa69b9d099555b9ca0b8b69;p=thirdparty%2Fsqlite.git wasm: corrected the propagation of text/blob values via UDFs. DB.exec()'s sql may now be an array of strings which get concatenated together before passing it on to sqlite3_prepare_v2(). DB.exec()'s callback now applies to the first statement which has result columns instead of only the first statement. Fixed a precedence but which caused isInt32() to report false positives. FossilOrigin-Name: 37a8fecb56793fbd3763a2240a0bd62b639c811934d3af2ef0e5ff579073d632 --- diff --git a/ext/fiddle/sqlite3-api.js b/ext/fiddle/sqlite3-api.js index 74f3e35480..f4d0b68d00 100644 --- a/ext/fiddle/sqlite3-api.js +++ b/ext/fiddle/sqlite3-api.js @@ -186,7 +186,12 @@ Module.postRun.push(function(namespace/*the module object, the target for module-init-internal state which isn't normally visible to us. */ - allocate: (slab, allocator=SQM.ALLOC_NORMAL)=>SQM.allocate(slab, allocator) + allocate: (slab, allocator=SQM.ALLOC_NORMAL)=>SQM.allocate(slab, allocator), + /** + The buffer which holds the heap memory managed by the + emscripten-installed allocator. + */ + HEAP8: SQM.HEAP8 } }; [/* C-side functions to bind. Each entry is an array with 3 or 4 @@ -485,7 +490,7 @@ Module.postRun.push(function(namespace/*the module object, the target for /** Returns true if n is a 32-bit (signed) integer, else false. */ const isInt32 = function(n){ - return (n===n|0 && n<0xFFFFFFFF) ? true : undefined; + return (n===(n|0) && n<0xFFFFFFFF) ? true : undefined; }; /** @@ -521,6 +526,8 @@ Module.postRun.push(function(namespace/*the module object, the target for }; if(out.sql instanceof Uint8Array){ out.sql = uint8ToString(out.sql); + }else if(Array.isArray(out.sql)){ + out.sql = out.sql.join(''); }else if('string'!==typeof out.sql){ toss("Missing SQL argument."); } @@ -713,7 +720,10 @@ Module.postRun.push(function(namespace/*the module object, the target for properties: - .sql = the SQL to run (unless it's provided as the first - argument). This must be of type string or Uint8Array. + argument). This must be of type string, Uint8Array, or an + array of strings (in which case they're concatenated + together as-is, with no separator between elements, + before evaluation). - .bind = a single value valid as an argument for Stmt.bind(). This is ONLY applied to the FIRST non-empty @@ -721,12 +731,13 @@ Module.postRun.push(function(namespace/*the module object, the target for parameters. (Empty statements are skipped entirely.) - .callback = a function which gets called for each row of - the FIRST statement in the SQL (if it has any result - rows). The second argument passed to the callback is - always the current Stmt object (so that the caller - may collect column names, or similar). The first - argument passed to the callback defaults to the current - Stmt object but may be changed with ... + the FIRST statement in the SQL which has result + _columns_, but only if that statement has any result + _rows_. The second argument passed to the callback is + always the current Stmt object (so that the caller may + collect column names, or similar). The first argument + passed to the callback defaults to the current Stmt + object but may be changed with ... - .rowMode = a string describing what type of argument should be passed as the first argument to the callback. A @@ -796,7 +807,7 @@ Module.postRun.push(function(namespace/*the module object, the target for stmt.bind(bind); bind = null; } - if(opt.callback && null!==rowMode){ + if(opt.callback && null!==rowMode && stmt.columnCount){ while(stmt.step()){ stmt._isLocked = true; callback(arg.cbArg(stmt), stmt); @@ -907,15 +918,15 @@ Module.postRun.push(function(namespace/*the module object, the target for case api.SQLITE_FLOAT: arg = api.sqlite3_value_double(pVal); break; - case SQLITE_TEXT: + case api.SQLITE_TEXT: arg = api.sqlite3_value_text(pVal); break; - case SQLITE_BLOB:{ - const n = api.sqlite3_value_bytes(ptr); - const pBlob = api.sqlite3_value_blob(ptr); + case api.SQLITE_BLOB:{ + const n = api.sqlite3_value_bytes(pVal); + const pBlob = api.sqlite3_value_blob(pVal); arg = new Uint8Array(n); let i; - for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i]; + for(i = 0; i < n; ++i) arg[i] = api.wasm.HEAP8[pBlob+i]; break; } default: @@ -948,7 +959,7 @@ Module.postRun.push(function(namespace/*the module object, the target for api.wasm.allocate(val); api.sqlite3_result_blob(pCx, pBlob, val.length, api.SQLITE_TRANSIENT); - api.wasm._free(blobptr); + api.wasm._free(pBlob); break; } // else fall through @@ -1082,7 +1093,7 @@ Module.postRun.push(function(namespace/*the module object, the target for const affirmParamIndex = function(stmt,key){ const n = ('number'===typeof key) ? key : api.sqlite3_bind_parameter_index(stmt._pStmt, key); - if(0===n || (n===key && (n!==(n|0)/*floating point*/))){ + if(0===n || !isInt32(n)){ toss("Invalid bind() parameter name: "+key); } else if(n<1 || n>stmt.parameterCount) toss("Bind index",key,"is out of range."); @@ -1456,7 +1467,7 @@ Module.postRun.push(function(namespace/*the module object, the target for 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]; + for(let i = 0; i < n; ++i) rc[i] = api.wasm.HEAP8[ptr + i]; if(n && this.db._blobXfer instanceof Array){ /* This is an optimization soley for the Worker-based API. These values will be diff --git a/ext/fiddle/testing1.js b/ext/fiddle/testing1.js index ceea0e6200..0b178d8d9e 100644 --- a/ext/fiddle/testing1.js +++ b/ext/fiddle/testing1.js @@ -66,11 +66,14 @@ let list = []; db.exec({ - sql:`CREATE TABLE t(a,b); -INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, + sql:['CREATE TABLE t(a,b);', + "INSERT INTO t(a,b) VALUES(1,2),(3,4),", + "(?,?),('blob',X'6869');" + ], multi: true, saveSql: list, bind: [5,6] + /* Achtung: ^^^ bind support might be removed from multi-mode exec. */ }); T.assert(2 === list.length); //log("Exec'd SQL:", list); @@ -82,23 +85,24 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, columnNames: colNames, callback: function(row,stmt){ ++counter; - T.assert(row.a%2 && row.a<6); + T.assert((row.a%2 && row.a<6) || 'blob'===row.a); } }); T.assert(2 === colNames.length) .assert('a' === colNames[0]) - .assert(3 === counter) - .assert(3 === list.length); + .assert(4 === counter) + .assert(4 === list.length); list.length = 0; db.exec("SELECT a a, b b FROM t",{ rowMode: 'array', callback: function(row,stmt){ ++counter; T.assert(Array.isArray(row)) - .assert(0===row[1]%2 && row[1]<7); + .assert((0===row[1]%2 && row[1]<7) + || (row[1] instanceof Uint8Array)); } }); - T.assert(6 === counter); + T.assert(8 === counter); }; const testUDF = function(db){ @@ -106,7 +110,7 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, db.createFunction("foo",function(a,b){return a+b}); T.assert(7===db.selectValue("select foo(3,4)")). assert(5===db.selectValue("select foo(3,?)",2)). - assert(5===db.selectValue("select foo(?,?)",[1,4])). + assert(5===db.selectValue("select foo(?,?2)",[1,4])). assert(5===db.selectValue("select foo($a,$b)",{$a:0,$b:5})); db.createFunction("bar", { arity: -1, @@ -115,15 +119,26 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, for(let i = 0; i < arguments.length; ++i) rc += arguments[i]; return rc; } + }).createFunction({ + name: "asis", + callback: function(arg){ + return arg; + } }); log("Testing DB::selectValue() w/ UDF..."); T.assert(0===db.selectValue("select bar()")). assert(1===db.selectValue("select bar(1)")). assert(3===db.selectValue("select bar(1,2)")). - assert(-1===db.selectValue("select bar(1,2,-4)")); + assert(-1===db.selectValue("select bar(1,2,-4)")). + assert('hi'===db.selectValue("select asis('hi')")); + let blob = db.selectValue("select asis(X'6869')"); + T.assert(blob instanceof Uint8Array). + assert(2 === blob.length). + assert(0x68==blob[0] && 0x69==blob[1]); const eqApprox = function(v1,v2,factor=0.05){ + //log('eqApprox',v1, v2); return v1>=(v2-factor) && v1<=(v2+factor); }; @@ -132,7 +147,8 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, assert(null === db.selectValue("select ?",null)). assert(null === db.selectValue("select ?",[null])). assert(null === db.selectValue("select $a",{$a:null})). - assert(eqApprox(3.1,db.selectValue("select 3.0 + 0.1"))) + assert(eqApprox(3.1,db.selectValue("select 3.0 + 0.1"))). + assert(eqApprox(1.3,db.selectValue("select asis(1 + 0.3)"))) ; }; diff --git a/manifest b/manifest index 6a4922ca36..ab24710ad4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\swasm\sdoc\stweaks. -D 2022-06-25T02:54:20.783 +C wasm:\scorrected\sthe\spropagation\sof\stext/blob\svalues\svia\sUDFs.\sDB.exec()'s\ssql\smay\snow\sbe\san\sarray\sof\sstrings\swhich\sget\sconcatenated\stogether\sbefore\spassing\sit\son\sto\ssqlite3_prepare_v2().\sDB.exec()'s\scallback\snow\sapplies\sto\sthe\sfirst\sstatement\swhich\shas\sresult\scolumns\sinstead\sof\sonly\sthe\sfirst\sstatement.\sFixed\sa\sprecedence\sbut\swhich\scaused\sisInt32()\sto\sreport\sfalse\spositives. +D 2022-06-25T03:53:43.503 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -65,11 +65,11 @@ F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba83 F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8 F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf -F ext/fiddle/sqlite3-api.js c54f0ee6a65a59284e6b0184f556376b35f5996b6b82607d7275a95f759a380a +F ext/fiddle/sqlite3-api.js b706be1f777a4508f9e3e237301e11c108933ae651b3fa599bf047cac34b5bdb F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421 F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a -F ext/fiddle/testing1.js 2ba8d14b335cdfc694757c25a3ffcfa76f4696bb21187db5c12ea6e505c44af0 +F ext/fiddle/testing1.js 3e5f1fb22764ec735396db518b85ea6b3070f6c2da479cba69c8d8e89f8299f8 F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4 F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e @@ -1978,8 +1978,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 ab3e50dab4d71557ab5d179bbd6caf7fb61ab7c51dffc8e4714441c189ce3e5c -R bbdb5fff926c789b277af363fc1aa00d +P 42dc500819bfc1308a9542aa2cae4f6dfd98a29237c59cec82e0e6f9e0bf3779 +R a152bf70e7d16ac14f437dceeb13aaae U stephan -Z 54385a773b385e5557f29a3390198b0f +Z 385f37f8ee9972f16da6e6bace96df52 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 66b7720610..cd66bdca9e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -42dc500819bfc1308a9542aa2cae4f6dfd98a29237c59cec82e0e6f9e0bf3779 \ No newline at end of file +37a8fecb56793fbd3763a2240a0bd62b639c811934d3af2ef0e5ff579073d632 \ No newline at end of file