Add more JS tests. Flesh out the aggregate UDF tests to use sqlite3_aggregate_context() so that they can each be used multiple times in the same statement. Add sqlite3_js_aggregate_context() convenience helper.
FossilOrigin-Name: 9d034ef5e1bab7c9651c2450dc85765fa6365d3f1414c711550de858ff8b3ece
This commit is contained in:
parent
875b95d4f5
commit
96b6371d70
@ -149,9 +149,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
|
||||
if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
|
||||
oflags |= capi.SQLITE_OPEN_EXRESCODE;
|
||||
const scope = wasm.scopedAllocPush();
|
||||
const stack = wasm.pstack.pointer;
|
||||
try {
|
||||
const pPtr = wasm.allocPtr() /* output (sqlite3**) arg */;
|
||||
const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */;
|
||||
const pVfsName = vfsName ? (
|
||||
('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName))
|
||||
): 0;
|
||||
@ -163,21 +163,19 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
__dbTraceToConsole, 0);
|
||||
}
|
||||
// Check for per-VFS post-open SQL...
|
||||
wasm.setPtrValue(pPtr, 0);
|
||||
if(0===capi.sqlite3_file_control(
|
||||
pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, pPtr
|
||||
)){
|
||||
const postInitSql = __vfsPostOpenSql[wasm.getPtrValue(pPtr)];
|
||||
if(postInitSql){
|
||||
rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0);
|
||||
checkSqlite3Rc(pDb, rc);
|
||||
}
|
||||
const pVfs = capi.sqlite3_js_db_vfs(pDb);
|
||||
//console.warn("Opened db",fn,"with vfs",vfsName,pVfs);
|
||||
if(!pVfs) toss3("Internal error: cannot get VFS for new db handle.");
|
||||
const postInitSql = __vfsPostOpenSql[pVfs];
|
||||
if(postInitSql){
|
||||
rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0);
|
||||
checkSqlite3Rc(pDb, rc);
|
||||
}
|
||||
}catch( e ){
|
||||
if( pDb ) capi.sqlite3_close_v2(pDb);
|
||||
throw e;
|
||||
}finally{
|
||||
wasm.scopedAllocPop(scope);
|
||||
wasm.pstack.restore(stack);
|
||||
}
|
||||
this.filename = fnJs;
|
||||
__ptrMap.set(this, pDb);
|
||||
|
@ -280,6 +280,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
this.name = 'WasmAllocError';
|
||||
}
|
||||
};
|
||||
/**
|
||||
Functionally equivalent to the WasmAllocError constructor but may
|
||||
be used as part of an expression, e.g.:
|
||||
|
||||
```
|
||||
return someAllocatingFunction(x) || WasmAllocError.toss(...);
|
||||
```
|
||||
*/
|
||||
WasmAllocError.toss = (...args)=>{
|
||||
throw new WasmAllocError(args.join(' '));
|
||||
};
|
||||
@ -508,6 +516,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
"flexible-string" argument converter.
|
||||
*/
|
||||
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
|
||||
|
||||
/**
|
||||
Various internal-use utilities are added here as needed. They
|
||||
are bound to an object only so that we have access to them in
|
||||
@ -1024,6 +1033,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
this.name = 'SQLite3Error';
|
||||
}
|
||||
};
|
||||
/**
|
||||
Functionally equivalent to the SQLite3Error constructor but may
|
||||
be used as part of an expression, e.g.:
|
||||
|
||||
```
|
||||
return someFunction(x) || SQLite3Error.toss(...);
|
||||
```
|
||||
*/
|
||||
SQLite3Error.toss = (...args)=>{
|
||||
throw new SQLite3Error(args.join(' '));
|
||||
};
|
||||
@ -1096,8 +1113,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
pointer. The 3rd argument specifies the database name of the
|
||||
given database connection to check, defaulting to the main db.
|
||||
|
||||
The 2nd and 3rd arguments may either be a JS string or a C-string
|
||||
allocated from the wasm environment.
|
||||
The 2nd and 3rd arguments may either be a JS string or a WASM
|
||||
C-string. If the 2nd argument is a NULL WASM pointer, the default
|
||||
VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is
|
||||
assumed.
|
||||
|
||||
The truthy value it returns is a pointer to the `sqlite3_vfs`
|
||||
object.
|
||||
@ -1112,17 +1131,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
const pK = capi.sqlite3_vfs_find(vfsName);
|
||||
if(!pK) return false;
|
||||
else if(!pDb){
|
||||
return capi.sqlite3_vfs_find(0)===pK ? pK : false;
|
||||
}
|
||||
const ppVfs = wasm.allocPtr();
|
||||
try{
|
||||
return (
|
||||
(0===capi.sqlite3_file_control(
|
||||
pDb, dbName, capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
|
||||
)) && (wasm.getPtrValue(ppVfs) === pK)
|
||||
) ? pK : false;
|
||||
}finally{
|
||||
wasm.dealloc(ppVfs);
|
||||
return pK===capi.sqlite3_vfs_find(0) ? pK : false;
|
||||
}else{
|
||||
return pK===capi.sqlite3_js_db_vfs(pDb) ? pK : false;
|
||||
}
|
||||
}catch(e){
|
||||
/* Ignore - probably bad args to a wasm-bound function. */
|
||||
@ -1179,14 +1190,32 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
? wasm.heap8u().slice(pOut, pOut + Number(nOut))
|
||||
: new Uint8Array();
|
||||
return rc;
|
||||
}catch(e){
|
||||
console.error('internal error?',e);
|
||||
throw w;
|
||||
}finally{
|
||||
if(pOut) wasm.exports.sqlite3_free(pOut);
|
||||
wasm.pstack.restore(stack);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Given a `sqlite3*` and a database name (JS string or WASM
|
||||
C-string pointer, which may be 0), returns a pointer to the
|
||||
sqlite3_vfs responsible for it. If the given db name is null/0,
|
||||
or not provided, then "main" is assumed.
|
||||
*/
|
||||
capi.sqlite3_js_db_vfs =
|
||||
(dbPointer, dbName=0)=>wasm.sqlite3_wasm_db_vfs(dbPointer, dbName);
|
||||
|
||||
/**
|
||||
A thin wrapper around capi.sqlite3_aggregate_context() which
|
||||
behaves the same except that it throws a WasmAllocError if that
|
||||
function returns 0.
|
||||
*/
|
||||
capi.sqlite3_js_aggregate_context = (pCtx, n)=>{
|
||||
return capi.sqlite3_aggregate_context(pCtx, n)
|
||||
|| WasmAllocError.toss(
|
||||
"Cannot allocate",n,"bytes for sqlite3_aggregate_context()"
|
||||
);
|
||||
};
|
||||
|
||||
if( capi.util.isMainWindow() ){
|
||||
/* Features specific to the main window thread... */
|
||||
|
@ -231,7 +231,7 @@
|
||||
|
||||
If the `dbId` does not refer to an opened ID, this is a no-op. If
|
||||
the `args` object contains a truthy `unlink` value then the database
|
||||
will unlinked (deleted) after closing it. The inability to close a
|
||||
will be unlinked (deleted) after closing it. The inability to close a
|
||||
db (because it's not opened) or delete its file does not trigger an
|
||||
error.
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
** want undefined. Please keep these alphabetized.
|
||||
*/
|
||||
#undef SQLITE_OMIT_DESERIALIZE
|
||||
#undef SQLITE_OMIT_MEMORYDB
|
||||
|
||||
/*
|
||||
** Define any SQLITE_... config defaults we want if they aren't
|
||||
|
@ -1083,6 +1083,47 @@
|
||||
.assert(0===this.db.openStatementCount());
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('sqlite3_js_...()', function(){
|
||||
const db = this.db;
|
||||
if(1){
|
||||
const vfsList = capi.sqlite3_js_vfs_list();
|
||||
T.assert(vfsList.length>1);
|
||||
T.assert('string'===typeof vfsList[0]);
|
||||
|
||||
for(const v of vfsList){
|
||||
T.assert('string' === typeof v)
|
||||
.assert(capi.sqlite3_vfs_find(v) > 0);
|
||||
}
|
||||
}
|
||||
/**
|
||||
Trivia: the magic db name ":memory:" does not actually use the
|
||||
"memdb" VFS unless "memdb" is _explicitly_ provided as the VFS
|
||||
name. Instead, it uses the default VFS with an in-memory btree.
|
||||
Thus this.db's VFS may not be memdb even though it's an in-memory
|
||||
db.
|
||||
*/
|
||||
const pVfsMem = capi.sqlite3_vfs_find('memdb'),
|
||||
pVfsDflt = capi.sqlite3_vfs_find(0),
|
||||
pVfsDb = capi.sqlite3_js_db_vfs(db.pointer);
|
||||
T.assert(pVfsMem > 0)
|
||||
.assert(pVfsDflt > 0)
|
||||
.assert(pVfsDb > 0)
|
||||
.assert(pVfsMem !== pVfsDflt
|
||||
/* memdb lives on top of the default vfs */)
|
||||
.assert(':memory:' === db.filename)
|
||||
.assert(pVfsDb === pVfsDflt || pVfsdb === pVfsMem)
|
||||
;
|
||||
/*const vMem = new capi.sqlite3_vfs(pVfsMem),
|
||||
vDflt = new capi.sqlite3_vfs(pVfsDflt),
|
||||
vDb = new capi.sqlite3_vfs(pVfsDb);*/
|
||||
const duv = capi.sqlite3_js_db_uses_vfs;
|
||||
T.assert(pVfsDflt === duv(db.pointer, 0)
|
||||
|| pVfsMem === duv(db.pointer,0))
|
||||
.assert(!duv(db.pointer, "foo"))
|
||||
;
|
||||
}/*sqlite3_js_...()*/)
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
.t('Table t', function(sqlite3){
|
||||
const db = this.db;
|
||||
@ -1168,6 +1209,14 @@
|
||||
.assert(0==e.message.indexOf('Cannot prepare empty'));
|
||||
}
|
||||
})
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('sqlite3_js_db_export()', function(){
|
||||
const db = this.db;
|
||||
const xp = capi.sqlite3_js_db_export(db.pointer);
|
||||
T.assert(xp instanceof Uint8Array)
|
||||
.assert(xp.byteLength>0)
|
||||
.assert(0 === xp.byteLength % 512);
|
||||
}/*sqlite3_js_db_export()*/)
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
.t('Scalar UDFs', function(sqlite3){
|
||||
@ -1228,39 +1277,40 @@
|
||||
name: 'Aggregate UDFs',
|
||||
test: function(sqlite3){
|
||||
const db = this.db;
|
||||
const aggState = {summer: 0, summerN: 0};
|
||||
const sjac = capi.sqlite3_js_aggregate_context;
|
||||
db.createFunction({
|
||||
name: 'summer',
|
||||
xStep: function(pCtx, n){
|
||||
aggState.summer += n;
|
||||
const pAgg = sjac(pCtx, 4);
|
||||
wasm.setMemValue(pAgg, wasm.getMemValue(pAgg,'i32') + Number(n), 'i32');
|
||||
},
|
||||
xFinal: function(pCtx){
|
||||
const rc = aggState.summer;
|
||||
aggState.summer = 0;
|
||||
return rc;
|
||||
}
|
||||
xFinal: (pCtx)=>wasm.getMemValue(sjac(pCtx, 4),'i32')
|
||||
});
|
||||
let v = db.selectValue([
|
||||
"with cte(v) as (",
|
||||
"select 3 union all select 5 union all select 7",
|
||||
") select summer(v) from cte"
|
||||
") select summer(v), summer(v+1) from cte"
|
||||
/* ------------------^^^^^^^^^^^ ensures that we're handling
|
||||
sqlite3_aggregate_context() properly. */
|
||||
]);
|
||||
T.assert(15===v);
|
||||
T.mustThrowMatching(()=>db.selectValue("select summer(1,2)"),
|
||||
/wrong number of arguments/);
|
||||
|
||||
db.createFunction({
|
||||
name: 'summerN',
|
||||
arity: -1,
|
||||
xStep: function(pCtx, ...args){
|
||||
for(const v of args) aggState.summerN += v;
|
||||
const pAgg = sjac(pCtx, 4);
|
||||
let sum = wasm.getMemValue(pAgg, 'i32');
|
||||
for(const v of args) sum += Number(v);
|
||||
wasm.setMemValue(pAgg, sum, 'i32');
|
||||
},
|
||||
xFinal: function(pCtx){
|
||||
const rc = aggState.summerN;
|
||||
aggState.summerN = 0;
|
||||
return rc;
|
||||
return wasm.getMemValue(sjac(pCtx, 4),'i32')
|
||||
}
|
||||
});
|
||||
T.assert(18===db.selectValue('select summerN(1,8,9)'));
|
||||
T.assert(18===db.selectValue('select summerN(1,8,9), summerN(2,3,4)'));
|
||||
T.mustThrowMatching(()=>{
|
||||
db.createFunction('nope',{
|
||||
xFunc: ()=>{}, xStep: ()=>{}
|
||||
@ -1284,6 +1334,30 @@
|
||||
}
|
||||
}/*aggregate UDFs*/)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t({
|
||||
name: 'Aggregate UDFs (64-bit)',
|
||||
predicate: ()=>wasm.bigIntEnabled,
|
||||
test: function(sqlite3){
|
||||
const db = this.db;
|
||||
const sjac = capi.sqlite3_js_aggregate_context;
|
||||
db.createFunction({
|
||||
name: 'summer64',
|
||||
xStep: function(pCtx, n){
|
||||
const pAgg = sjac(pCtx, 8);
|
||||
wasm.setMemValue(pAgg, wasm.getMemValue(pAgg,'i64') + BigInt(n), 'i64');
|
||||
},
|
||||
xFinal: (pCtx)=>wasm.getMemValue(sjac(pCtx, 8),'i64')
|
||||
});
|
||||
let v = db.selectValue([
|
||||
"with cte(v) as (",
|
||||
"select 3 union all select 5 union all select 7",
|
||||
") select summer64(v*10), summer64(v+1) from cte"
|
||||
]);
|
||||
T.assert(150n===BigInt(v));
|
||||
}
|
||||
}/*aggregate UDFs*/)
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
.t({
|
||||
name: 'Window UDFs (tests are TODO)',
|
||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Apply\smagic.txt\scorrection\sand\saddition\sreported\sin\s[forum\spost\s2d2366a04a0385|forum:2d2366a04a0385].
|
||||
D 2022-10-20T18:58:14.289
|
||||
C Add\smore\sJS\stests.\sFlesh\sout\sthe\saggregate\sUDF\stests\sto\suse\ssqlite3_aggregate_context()\sso\sthat\sthey\scan\seach\sbe\sused\smultiple\stimes\sin\sthe\ssame\sstatement.\sAdd\ssqlite3_js_aggregate_context()\sconvenience\shelper.
|
||||
D 2022-10-20T21:28:31.440
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -485,14 +485,14 @@ F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1
|
||||
F ext/wasm/api/pre-js.js 151e0616614a49f3db19ed544fa13b38c87c108959fbcd4029ea8399a562d94f
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 4d07a7524dc9b7b050acfde57163e839243ad2383bd7ee0de0178b1b3e988588
|
||||
F ext/wasm/api/sqlite3-api-glue.js 0b5240bd325d2561f269cd0d82bf686336526e5e276251c2241adfbda802abf8
|
||||
F ext/wasm/api/sqlite3-api-oo1.js dc9b6a61649ad32836044de388c5248790239d62ced4e1116023135fcb0fc68b
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 0e278d131dad72e9eb348a3dda6a4ff734a9e08925b4ed7e6e5a688d2edaf525
|
||||
F ext/wasm/api/sqlite3-api-opfs.js 22d60ba956e873b65e2e0591e239178082bd53a6d563c3c58db7dc03e562e8f7
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 1366d538a7b388c299a389f441a79cf0b18af50208343545bd318936b6232acd
|
||||
F ext/wasm/api/sqlite3-api-worker1.js cb07b321164483524a27cf2207d4358b905703c410fcd8256e0acca5ab2fffb2
|
||||
F ext/wasm/api/sqlite3-api-prologue.js bb7a98a8c62545bf07b5fdee831c0d40c86f98c0094b00d8497a9de8976a0544
|
||||
F ext/wasm/api/sqlite3-api-worker1.js a7f38f03275d6c27ab2aef3e83215d3c97ce09c43e6904df47c3764d9d4572b4
|
||||
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 206ce6bbc3c30ad51a37d9c25e3a2712e70b586e0f9a2cf8cb0b9619017c2671
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c e1fcda97775dd149b4a2e0a4f16a22cede96daa5a91795ba214bdb3c2e680f4a
|
||||
F ext/wasm/api/sqlite3-wasm.c dde771a10e6a8d01ae516d32abc4cdd2d542cb3339e35c5b4be8ebbc1dc1ccc1
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
|
||||
F ext/wasm/api/sqlite3-worker1.js dbe54b69c1520a2d25eae148cd2750ded2dd7f219ea4ee46f83e0a851dca5974
|
||||
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
|
||||
@ -533,7 +533,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
|
||||
F ext/wasm/tester1-worker.html 048c341f124fdb61ca14dfd1bd1f78742490f208aa3bb1e84399f83f1e7e6a74
|
||||
F ext/wasm/tester1.html 37ccc958fa0d95074af2d72b7241c8e2d982bbec6dda4dc790241af3d933c3b6
|
||||
F ext/wasm/tester1.js 44d71175e2941bf1d7c27afa0c395fe81c83cbd74cd10e34e0688dd833042f1e
|
||||
F ext/wasm/tester1.js 6e1c8e4d48add1d49fb7901dfee04fdf6cdd5bc39adcbf15c097f61b581a893c
|
||||
F ext/wasm/version-info.c 5fa356d38859d71a0369b5c37e1935def7413fcc8a4e349a39d9052c1d0479f4
|
||||
F ext/wasm/wasmfs.make ee0004813e16c283ff633e08b482008d56adf9b7d42f6c5612f7ab002b924f69
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
@ -2036,8 +2036,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 ac9af71b72a749b0a040273a88480d56f49570b569389a4ea20cc055f494d8ff
|
||||
R b44d84c26c2f9b06f7e9b798ca72a049
|
||||
P 9bf26e2aa3579f354ed2d314e1bf3e3ef117cbd71500ef5f76caa1de5cce1edc
|
||||
R dbe97bbb80fe6071199461fb846ef1aa
|
||||
U stephan
|
||||
Z e01cdee717736d0716b4f5500835f6af
|
||||
Z 83bd13decfea9b8d2129079a8ff06725
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
9bf26e2aa3579f354ed2d314e1bf3e3ef117cbd71500ef5f76caa1de5cce1edc
|
||||
9d034ef5e1bab7c9651c2450dc85765fa6365d3f1414c711550de858ff8b3ece
|
Loading…
x
Reference in New Issue
Block a user