}
if(out.opt.callback || out.opt.resultRows){
switch((undefined===out.opt.rowMode)
- ? 'stmt' : out.opt.rowMode) {
+ ? 'array' : out.opt.rowMode) {
case 'object': out.cbArg = (stmt)=>stmt.get({}); break;
case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
case 'stmt':
if(Array.isArray(out.opt.resultRows)){
- toss3("Invalid rowMode for resultRows array: must",
+ toss3("Invalid rowMode for a resultRows array: must",
"be one of 'array', 'object',",
- "or a result column number.");
+ "a result column number, or column name reference.");
}
out.cbArg = (stmt)=>stmt;
break;
if(util.isInt32(out.opt.rowMode)){
out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
break;
- }
- /*
- TODO?: how can we define rowMode such that it uses
- rowMode of 'object' and returns a given named field from
- the object. Something like:
-
- if(?what goes here?){
- out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]}
- .bind({obj:{}, colName: ???what goes here???}});
+ }else if('string'===typeof out.opt.rowMode && out.opt.rowMode.length>1){
+ /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */
+ const prefix = out.opt.rowMode[0];
+ if(':'===prefix || '@'===prefix || '$'===prefix){
+ out.cbArg = function(stmt){
+ return stmt.get(this.obj)[this.colName];
+ }.bind({obj:{}, colName: out.opt.rowMode.substr(1)})
break;
}
-
- Maybe rowMode:['colName1',... 'colNameN']? That could be
- ambiguous: might mean "return an object with just these
- columns".
- */
+ }
toss3("Invalid rowMode:",out.opt.rowMode);
}
}
if(rowTarget) rowTarget.push(row);
if(opt.callback){
stmt._isLocked = true;
- opt.callback(row, stmt);
+ opt.callback(row,stmt);
stmt._isLocked = false;
}
}
if that statement has any result _rows_. The callback's "this"
is the options object. 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 = either a string describing what type of argument
- should be passed as the first argument to the callback or an
- integer representing a result column index. A `rowMode` of
- 'object' causes the results of `stmt.get({})` to be passed to
- the `callback` and/or appended to `resultRows`. A value of
- 'array' causes the results of `stmt.get([])` to be passed to
- passed on. A value of 'stmt' is equivalent to the default,
- passing the current Stmt to the callback (noting that it's
- always passed as the 2nd argument), but this mode will trigger
- an exception if `resultRows` is an array. If `rowMode` is an
- integer, only the single value from that result column will be
- passed on. Any other value for the option triggers an
- exception.
+ may collect column names, or similar). The 2nd argument to the
+ callback is always the Stmt instance, as it's needed if the
+ caller wants to fetch the column names or some such. The first
+ argument passed to the callback defaults to the current Stmt
+ object but may be changed with ...
+
+ - .rowMode = may take one of several forms:
+
+ A) If `rowMode` is an integer, only the single value from that
+ result column (0-based) will be passed on.
+
+ B) A string describing what type of argument should be passed
+ as the first argument to the callback:
+
+ B.1) 'array' (the default) causes the results of
+ `stmt.get([])` to be passed to passed on and/or appended to
+ `resultRows`.
+
+ B.2) 'object' causes the results of `stmt.get({})` to be
+ passed to the `callback` and/or appended to `resultRows`.
+
+ B.3) 'stmt' causes the current Stmt to be passed to the
+ callback, but this mode will trigger an exception if
+ `resultRows` is an array because appending the statement to
+ the array would be unhelpful.
+
+ C) A string with a minimum length of 2 and leading character of
+ ':', '$', or '@' will fetch the row as an object, extract that
+ one field, and pass that field's value to the callback. Note
+ that these keys are case-sensitive so must match the case used
+ in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A'
+ would work but '$a' would not (it would result in `undefined`).
+
+ Any other `rowMode` value triggers an exception.
- .resultRows: if this is an array, it functions similarly to
the `callback` option: each row of the result set (if any) of
else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/);
while(wasm.getMemValue(pSql, 'i8')
- /* Maintenance reminder: ^^^^ _must_ be i8 or else we
+ /* Maintenance reminder:^^^ _must_ be 'i8' or else we
will very likely cause an endless loop. What that's
doing is checking for a terminating NUL byte. If we
use i32 or similar then we read 4 bytes, read stuff
stmt._isLocked = true;
const row = arg.cbArg(stmt);
if(resultRows) resultRows.push(row);
- if(callback) callback(row, stmt);
+ if(callback) callback(row,stmt);
stmt._isLocked = false;
}
rowMode = undefined;
message type key, in which case a callback function will be
applied which posts each row result via:
- postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow})
+ postMessage({type: thatKeyType,
+ rowNumber: 1-based-#,
+ row: theRow,
+ columnNames: anArray
+ })
And, at the end of the result set (whether or not any result
rows were produced), it will post an identical message with
const rc = (
'string'===typeof ev.args
) ? {sql: ev.args} : (ev.args || Object.create(null));
- if(undefined===rc.rowMode){
- /* Since the default rowMode of 'stmt' is not useful
- for the Worker interface, we'll default to
- something else. */
- rc.rowMode = 'array';
- }else if('stmt'===rc.rowMode){
+ if('stmt'===rc.rowMode){
toss("Invalid rowMode for 'exec': stmt mode",
"does not work in the Worker API.");
}
// Part of a copy-avoidance optimization for blobs
db._blobXfer = wState.xfer;
}
- const callbackMsgType = rc.callback;
+ const theCallback = rc.callback;
let rowNumber = 0;
- if('string' === typeof callbackMsgType){
+ const hadColNames = !!rc.columnNames;
+ if('string' === typeof theCallback){
+ if(!hadColNames) rc.columnNames = [];
/* Treat this as a worker message type and post each
row as a message of that type. */
- rc.callback =
- (row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer);
+ rc.callback = function(row,stmt){
+ wState.post({
+ type: theCallback,
+ columnNames: rc.columnNames,
+ rowNumber: ++rowNumber,
+ row: row
+ }, wState.xfer);
+ }
}
try {
db.exec(rc);
if(rc.callback instanceof Function){
- rc.callback = callbackMsgType;
- wState.post({type: callbackMsgType, rowNumber: null, row: undefined});
+ rc.callback = theCallback;
+ /* Post a sentinel message to tell the client that the end
+ of the result set has been reached (possibly with zero
+ rows). */
+ wState.post({
+ type: theCallback,
+ columnNames: rc.columnNames,
+ rowNumber: null /*null to distinguish from "property not set"*/,
+ row: undefined /*undefined because null is a legal row value
+ for some rowType values, but undefined is not*/
+ });
}
}finally{
delete db._blobXfer;
- if(rc.callback){
- rc.callback = callbackMsgType;
- }
+ if(rc.callback) rc.callback = theCallback;
}
return rc;
}/*exec()*/,
if(undefined === f.counter) f.counter = 0;
if(null === ev.rowNumber){
/* End of result set. */
- T.assert(undefined === ev.row);
+ T.assert(undefined === ev.row)
+ .assert(2===ev.columnNames.length)
+ .assert('a'===ev.columnNames[0])
+ .assert('B'===ev.columnNames[1]);
}else{
T.assert(ev.rowNumber > 0);
++f.counter;
}
log("exec() result row:",ev);
- T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
+ T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B);
};
await wtest('exec',{
- sql: 'select a a, b b from t order by a',
+ sql: 'select a a, b B from t order by a limit 3',
callback: resultRowTest1,
rowMode: 'object'
}, function(ev){
resultRowTest1.counter = 0;
});
+ const resultRowTest2 = function f(ev){
+ if(null === ev.rowNumber){
+ /* End of result set. */
+ T.assert(undefined === ev.row)
+ .assert(1===ev.columnNames.length)
+ .assert('a'===ev.columnNames[0])
+ }else{
+ T.assert(ev.rowNumber > 0);
+ f.counter = ev.rowNumber;
+ }
+ log("exec() result row:",ev);
+ T.assert(null === ev.rowNumber || 'number' === typeof ev.row);
+ };
+ await wtest('exec',{
+ sql: 'select a a from t limit 3',
+ callback: resultRowTest2,
+ rowMode: 0
+ }, function(ev){
+ T.assert(3===resultRowTest2.counter);
+ });
+
+ const resultRowTest3 = function f(ev){
+ if(null === ev.rowNumber){
+ T.assert(3===ev.columnNames.length)
+ .assert('foo'===ev.columnNames[0])
+ .assert('bar'===ev.columnNames[1])
+ .assert('baz'===ev.columnNames[2]);
+ }else{
+ f.counter = ev.rowNumber;
+ T.assert('number' === typeof ev.row);
+ }
+ };
+ await wtest('exec',{
+ sql: "select 'foo' foo, a bar, 'baz' baz from t limit 2",
+ callback: resultRowTest3,
+ columnNames: [],
+ rowMode: ':bar'
+ }, function(ev){
+ log("exec() result row:",ev);
+ T.assert(2===resultRowTest3.counter);
+ });
+
await wtest('exec',{
multi: true,
sql:[
await wtest('close', (ev)=>{
T.assert(undefined === ev.result.filename);
- }).finally(()=>log("That's all, folks!"));
+ }).finally(()=>logHtml('',"That's all, folks!"));
}/*runTests2()*/;
if(undefined === f.counter) f.counter = 0;
if(null === ev.rowNumber){
/* End of result set. */
- T.assert(undefined === ev.row);
+ T.assert(undefined === ev.row)
+ .assert(Array.isArray(ev.columnNames))
+ .assert(ev.columnNames.length);
}else{
T.assert(ev.rowNumber > 0);
++f.counter;
-C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker).
-D 2022-08-24T18:39:46.246
+C Change\sDB.exec()\srowMode\sdefault\sfrom\s'stmt'\sto\s'array',\sper\s/chat\sdiscussion.\sAdd\sDB.exec()\srowMode\soption\sfor\sfetching\sa\sspecific\scolumn\sby\sname.\sAdd\sresult\scolumn\snames\sto\sworker1\sexec()\scallback\sinterface,\sas\sthere's\sotherwise\sno\sway\sto\sget\sthat\sinfo\sfrom\sa\sworker.
+D 2022-08-24T20:57:37.430
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de
F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3
-F ext/wasm/api/sqlite3-api-oo1.js 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc
+F ext/wasm/api/sqlite3-api-oo1.js 324b2f6817ff3711b59bd9505157f7a91fe319249d3b8b525c8254427c10504a
F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a
-F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca
+F ext/wasm/api/sqlite3-api-worker1.js 11a9e8b22147d948e338b25d21697178b4414dc0578fc9613aa5fc4bfe62f208
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
-F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d
+F ext/wasm/testing-worker1-promiser.js f4b0895b612606d04ae371d03a9ffe9ffa94a2a840da6e92742b2adf86f0783c
F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
-F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e
+F ext/wasm/testing2.js ab4ae24cd3ffe814370b35515aea426647a6f9d271c6542cf18e580470540615
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
-R 4d53a1266e528a954f73a792c3648256
+P 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
+R 4d9ca8433788ec1804950abdc80ba467
U stephan
-Z cffa6daad399e2d401f0fd69f4f04b4f
+Z aac659391ac05e725777e807ed0e05f4
# Remove this line to create a well-formed Fossil manifest.
-509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
\ No newline at end of file
+1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f
\ No newline at end of file