- `db`: the DB object which created the statement.
- - `columnCount`: the number of result columns in the query, or 0 for
- queries which cannot return results.
-
- - `parameterCount`: the number of bindable paramters in the query.
+ - `columnCount`: the number of result columns in the query, or 0
+ for queries which cannot return results. This property is a proxy
+ for sqlite3_column_count() and its use in loops should be avoided
+ because of the call overhead associated with that. The
+ `columnCount` is not cached when the Stmt is created because a
+ schema change made via a separate db connection between this
+ statement's preparation and when it is stepped may invalidate it.
+
+ - `parameterCount`: the number of bindable parameters in the query.
*/
const Stmt = function(){
if(BindTypes!==arguments[2]){
}
this.db = arguments[0];
__ptrMap.set(this, arguments[1]);
- this.columnCount = capi.sqlite3_column_count(this.pointer);
this.parameterCount = capi.sqlite3_bind_parameter_count(this.pointer);
};
with identical names.
- `callback` = a function which gets called for each row of the
- result set, but only if that statement has any result
- _rows_. The callback's "this" is the options object, noting
- that this function synthesizes one if the caller does not pass
- one to exec(). The second argument passed to the callback is
- always the current Stmt object, as it's needed if the caller
- wants to fetch the column names or some such (noting that they
- could also be fetched via `this.columnNames`, if the client
- provides the `columnNames` option). If the callback returns a
- literal `false` (as opposed to any other falsy value, e.g. an
- implicit `undefined` return), any ongoing statement-`step()`
- iteration stops without an error. The return value of the
- callback is otherwise ignored.
+ result set, but only if that statement has any result rows. The
+ callback's "this" is the options object, noting that this
+ function synthesizes one if the caller does not pass one to
+ exec(). The second argument passed to the callback is always
+ the current Stmt object, as it's needed if the caller wants to
+ fetch the column names or some such (noting that they could
+ also be fetched via `this.columnNames`, if the client provides
+ the `columnNames` option). If the callback returns a literal
+ `false` (as opposed to any other falsy value, e.g. an implicit
+ `undefined` return), any ongoing statement-`step()` iteration
+ stops without an error. The return value of the callback is
+ otherwise ignored.
ACHTUNG: The callback MUST NOT modify the Stmt object. Calling
any of the Stmt.get() variants, Stmt.getColumnName(), or
A.1) `'array'` (the default) causes the results of
`stmt.get([])` to be passed to the `callback` and/or appended
- to `resultRows`
+ to `resultRows`.
A.2) `'object'` causes the results of
`stmt.get(Object.create(null))` to be passed to the
A.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 downright unhelpful.
+ `resultRows` is an array because appending the transient
+ statement to the array would be downright unhelpful.
B) An integer, indicating a zero-based column in the result
row. Only that one single value will be passed on.
bind = null;
}
if(evalFirstResult && stmt.columnCount){
- /* Only forward SELECT results for the FIRST query
+ /* Only forward SELECT-style results for the FIRST query
in the SQL which potentially has them. */
+ let gotColNames = Array.isArray(
+ opt.columnNames
+ /* As reported in
+ https://sqlite.org/forum/forumpost/7774b773937cbe0a
+ we need to delay fetching of the column names until
+ after the first step() (if we step() at all) because
+ a schema change between the prepare() and step(), via
+ another connection, may invalidate the column count
+ and names. */) ? 0 : 1;
evalFirstResult = false;
- if(Array.isArray(opt.columnNames)){
- stmt.getColumnNames(opt.columnNames);
- }
if(arg.cbArg || resultRows){
for(; stmt.step(); stmt._isLocked = false){
+ if(0===gotColNames++) stmt.getColumnNames(opt.columnNames);
stmt._isLocked = true;
const row = arg.cbArg(stmt);
if(resultRows) resultRows.push(row);
}
stmt._isLocked = false;
}
+ if(0===gotColNames){
+ /* opt.columnNames was provided but we visited no result rows */
+ stmt.getColumnNames(opt.columnNames);
+ }
}else{
stmt.step();
}
capi.sqlite3_finalize(this.pointer);
__ptrMap.delete(this);
delete this._mayGet;
- delete this.columnCount;
delete this.parameterCount;
delete this.db;
delete this._isLocked;
}
if(Array.isArray(ndx)){
let i = 0;
- while(i<this.columnCount){
+ const n = this.columnCount;
+ while(i<n){
ndx[i] = this.get(i++);
}
return ndx;
}else if(ndx && 'object'===typeof ndx){
let i = 0;
- while(i<this.columnCount){
+ const n = this.columnCount;
+ while(i<n){
ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++);
}
return ndx;
);
},
/**
- If this statement potentially has result columns, this
- function returns an array of all such names. If passed an
- array, it is used as the target and all names are appended
- to it. Returns the target array. Throws if this statement
- cannot have result columns. This object's columnCount member
- holds the number of columns.
+ If this statement potentially has result columns, this function
+ returns an array of all such names. If passed an array, it is
+ used as the target and all names are appended to it. Returns
+ the target array. Throws if this statement cannot have result
+ columns. This object's columnCount property holds the number of
+ columns.
*/
getColumnNames: function(tgt=[]){
affirmColIndex(affirmStmtOpen(this),0);
- for(let i = 0; i < this.columnCount; ++i){
+ const n = this.columnCount;
+ for(let i = 0; i < n; ++i){
tgt.push(capi.sqlite3_column_name(this.pointer, i));
}
return tgt;
Object.defineProperty(Stmt.prototype, 'pointer', prop);
Object.defineProperty(DB.prototype, 'pointer', prop);
}
+ /**
+ A Stmt's columntCount property is an interceptor for
+ sqlite3_column_count().
+
+ This requires an unfortunate performance hit compared to caching
+ columnCount when the Stmt is created/prepared (as was done in
+ SQLite <=3.42.0), but is necessary in order to handle certain
+ corner cases, as described in
+ https://sqlite.org/forum/forumpost/7774b773937cbe0a.
+ */
+ Object.defineProperty(Stmt.prototype, 'columnCount', {
+ enumerable: false,
+ get: function(){return capi.sqlite3_column_count(this.pointer)},
+ set: ()=>toss3("The columnCount property is read-only.")
+ });
/** The OO API's public namespace. */
sqlite3.oo1 = {
-C Enhance\sthe\ssplit-sqlite3c.tcl\sscript\sso\sthat\sit\suses\ssingle-character\nextensions\son\ssubordinate\ssource\sfiles,\seven\swhen\sthe\snumber\sof\ssubordinates\nexceeds\s9.\s\sThis\sis\snot\syet\sneeded,\sbut\smight\sbe\ssoon.
-D 2023-05-10T16:04:04.601
+C Resolve\sthe\sJS-side\scorner\scase\sreported\sin\s[forum:7774b773937cbe0a\s|\sforum\spost\s7774b773937cbe0a]\sby\snot\scaching\soo1.Stmt.columnCount.
+D 2023-05-10T21:06:02.101
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
F ext/wasm/api/sqlite3-api-cleanup.js cc21e3486da748463e02bbe51e2464c6ac136587cdfd5aa00cd0b5385f6ca808
F ext/wasm/api/sqlite3-api-glue.js f1b2dcb944de5138bb5bd9a1559d2e76a4f3ec25260963d709e8237476688803
-F ext/wasm/api/sqlite3-api-oo1.js 2691a34a741015127b210954a1b9586764d3ff0c8a20f00fd15c00f339ecc79f
+F ext/wasm/api/sqlite3-api-oo1.js 4c7067c63eb1cdf78aee510243405f76816d737f679349482b865c714573ddac
F ext/wasm/api/sqlite3-api-prologue.js 17f4ec398ba34c5c666fea8e8c4eb82064a35b302f2f2eb355283cd8d3f68ed5
F ext/wasm/api/sqlite3-api-worker1.js 40a5b1813fcbe789f23ae196c833432c8c83e7054d660194ddfc51eab1c5b9bf
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e
F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25b4
-F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
+F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f
F ext/wasm/common/whwasmutil.js 749a1f81f85835e9a384e9706f2a955a7158f2b8cc9da33f41105cac7775a305
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
F ext/wasm/tester1-worker.html 258d08f1ba9cc2d455958751e26be833893cf9ff7853e9436e593e1f778a386b
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
-F ext/wasm/tester1.c-pp.js 587a18db0f794c594eb0fade37c92af3e2370501576bb08dafd8ed7d009b6516
+F ext/wasm/tester1.c-pp.js 2682abac377af9625f23837b7cfdbd0d8e0d5fee4d2acf3c98e1564785ccc479
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5bc17cbccdd369486fca049be9d7457e18f162c0e2f5475809ffc8f01c5fa9d2
-R 7825f403dc8e20a9b2cba11eaedce32c
-U drh
-Z 0780e89a4d80820b94f58c1bb18accfa
+P 4150e416263f24efcbfe68d5b1e15ec7e79df41dff0b6dfdc59f06ee9e205049
+R 4aa19a03c6b96f49d09013b0f165e4f7
+T *branch * oo1-no-cache-Stmt.columnCount
+T *sym-oo1-no-cache-Stmt.columnCount *
+T -sym-trunk * Cancelled\sby\sbranch.
+U stephan
+Z a06e6de30a703955eb7f478e54c3db62
# Remove this line to create a well-formed Fossil manifest.