diff options
author | stephan <stephan@noemail.net> | 2022-12-07 07:22:34 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2022-12-07 07:22:34 +0000 |
commit | 1eb1b59b893efb2fd4244d0116beb3c7db250d48 (patch) | |
tree | 170cfb4dee2fbff0cd96bf8761ee234b1e7d88b2 /ext/wasm/tester1.c-pp.js | |
parent | 30da58c5d66c386129a872ccd3a13b452c67717a (diff) | |
download | sqlite-1eb1b59b893efb2fd4244d0116beb3c7db250d48.tar.gz sqlite-1eb1b59b893efb2fd4244d0116beb3c7db250d48.zip |
Work on an alternate (slightly simpler) approach to binding JS vtabs. Non-eponymous vtabs are not working, for reasons as yet unknown.
FossilOrigin-Name: 6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23
Diffstat (limited to 'ext/wasm/tester1.c-pp.js')
-rw-r--r-- | ext/wasm/tester1.c-pp.js | 413 |
1 files changed, 297 insertions, 116 deletions
diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 910bbc94e..83b02522e 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -301,14 +301,12 @@ self.sqlite3InitModule = sqlite3InitModule; addTest: function(name, callback){ let predicate; if(1===arguments.length){ - const opt = arguments[0]; - predicate = opt.predicate; - name = opt.name; - callback = opt.test; + this.currentTestGroup.addTest(arguments[0]); + }else{ + this.currentTestGroup.addTest({ + name, predicate, test: callback + }); } - this.currentTestGroup.addTest({ - name, predicate, test: callback - }); return this; }, runTests: async function(sqlite3){ @@ -399,12 +397,21 @@ self.sqlite3InitModule = sqlite3InitModule; catch(e){T.assert("test ing ." === e.message)} try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) } - catch(e){ T.assert('SQLITE_SCHEMA' === e.message) } + catch(e){ + T.assert('SQLITE_SCHEMA' === e.message) + .assert(capi.SQLITE_SCHEMA === e.resultCode); + } try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) } catch(e){ - T.assert('SQLITE_CORRUPT'===e.message) + T.assert('SQLITE_CORRUPT' === e.message) + .assert(capi.SQLITE_CORRUPT === e.resultCode) .assert(true===e.cause); } + try{ sqlite3.SQLite3Error.toss("resultCode check") } + catch(e){ + T.assert(capi.SQLITE_ERROR === e.resultCode) + .assert('resultCode check' === e.message); + } }) //////////////////////////////////////////////////////////////////// .t('strglob/strlike', function(sqlite3){ @@ -988,6 +995,20 @@ self.sqlite3InitModule = sqlite3InitModule; const dbFile = '/tester1.db'; wasm.sqlite3_wasm_vfs_unlink(0, dbFile); const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c'); + db.onclose = { + disposeThese: [], + after: function(){ + while(this.disposeThese.length){ + const v = this.disposeThese.shift(); + console.debug("db.onclose cleaning up:",v); + if(wasm.isPtr(v)) wasm.dealloc(v); + else if(v instanceof sqlite3.StructBinder.StructType){ + v.dispose(); + } + } + } + }; + T.assert(Number.isInteger(db.pointer)) .mustThrowMatching(()=>db.pointer=1, /read-only/) .assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)) @@ -1539,10 +1560,96 @@ self.sqlite3InitModule = sqlite3InitModule; T.mustThrow(()=>db.exec("select * from foo.bar")); }) + //////////////////////////////////////////////////////////////////// + .t({ + name: 'C-side WASM tests', + predicate: ()=>(haveWasmCTests() || "Not compiled in."), + test: function(){ + const w = wasm, db = this.db; + const stack = w.scopedAllocPush(); + let ptrInt; + const origValue = 512; + const ptrValType = 'i32'; + try{ + ptrInt = w.scopedAlloc(4); + w.setMemValue(ptrInt,origValue, ptrValType); + const cf = w.xGet('sqlite3_wasm_test_intptr'); + const oldPtrInt = ptrInt; + //log('ptrInt',ptrInt); + //log('getMemValue(ptrInt)',w.getMemValue(ptrInt)); + T.assert(origValue === w.getMemValue(ptrInt, ptrValType)); + const rc = cf(ptrInt); + //log('cf(ptrInt)',rc); + //log('ptrInt',ptrInt); + //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType)); + T.assert(2*origValue === rc). + assert(rc === w.getMemValue(ptrInt,ptrValType)). + assert(oldPtrInt === ptrInt); + const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/; + const o64 = 0x010203040506/*>32-bit integer*/; + const ptrType64 = 'i64'; + if(w.bigIntEnabled){ + w.setMemValue(pi64, o64, ptrType64); + //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64); + const v64 = ()=>w.getMemValue(pi64,ptrType64) + //log("getMemValue(pi64)",v64()); + T.assert(v64() == o64); + //T.assert(o64 === w.getMemValue(pi64, ptrType64)); + const cf64w = w.xGet('sqlite3_wasm_test_int64ptr'); + cf64w(pi64); + //log("getMemValue(pi64)",v64()); + T.assert(v64() == BigInt(2 * o64)); + cf64w(pi64); + T.assert(v64() == BigInt(4 * o64)); + + const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2'); + T.assert(BigInt(2 * o64) === + biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError + in the call :/ */)); + + const pMin = w.scopedAlloc(16); + const pMax = pMin + 8; + const g64 = (p)=>w.getMemValue(p,ptrType64); + w.setMemValue(pMin, 0, ptrType64); + w.setMemValue(pMax, 0, ptrType64); + const minMaxI64 = [ + w.xCall('sqlite3_wasm_test_int64_min'), + w.xCall('sqlite3_wasm_test_int64_max') + ]; + T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)). + assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER)); + //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]); + w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax); + T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch"). + assert(g64(pMax) === minMaxI64[1], "int64 mismatch"); + //log("pMin",g64(pMin), "pMax",g64(pMax)); + w.setMemValue(pMin, minMaxI64[0], ptrType64); + T.assert(g64(pMin) === minMaxI64[0]). + assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). + assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); + const rxRange = /too big/; + T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))}, + rxRange). + mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))}, + (e)=>rxRange.test(e.message)); + }else{ + log("No BigInt support. Skipping related tests."); + log("\"The problem\" here is that we can manipulate, at the byte level,", + "heap memory to set 64-bit values, but we can't get those values", + "back into JS because of the lack of 64-bit integer support."); + } + }finally{ + const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1); + //log("x=",x,"y=",y,"z=",z); // just looking at the alignment + w.scopedAllocPop(stack); + } + } + }/* jaccwabyt-specific tests */) + //////////////////////////////////////////////////////////////////////// .t({ - name: 'Custom virtual tables', - predicate: ()=>wasm.bigIntEnabled, + name: 'virtual table #1', + predicate: ()=>!!capi.sqlite3_index_info, test: function(sqlite3){ warn("The vtab/module JS bindings are experimental and subject to change."); const vth = sqlite3.VtabHelper; @@ -1566,63 +1673,63 @@ self.sqlite3InitModule = sqlite3InitModule; pDb, "CREATE TABLE ignored(a,b)" ); if(0===rc){ - const t = vth.xWrapVtab(); + const t = vth.xVtab(); wasm.setPtrValue(ppVtab, t.pointer); - T.assert(t === vth.xWrapVtab(wasm.getPtrValue(ppVtab))); + T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab))); } return rc; }catch(e){ if(!(e instanceof sqlite3.WasmAllocError)){ wasm.setPtrValue(pzErr, wasm.allocCString(e.message)); } - return vth.xMethodError('xConnect',e); + return vth.xError('xConnect',e); } }, xDisconnect: function(pVtab){ try { - const t = vth.xWrapVtab(pVtab, true); + const t = vth.xVtab(pVtab, true); t.dispose(); return 0; }catch(e){ - return vth.xMethodError('xDisconnect',e); + return vth.xError('xDisconnect',e); } }, xOpen: function(pVtab, ppCursor){ try{ - const t = vth.xWrapVtab(pVtab), c = vth.xWrapCursor(); + const t = vth.xVtab(pVtab), c = vth.xCursor(); T.assert(t instanceof capi.sqlite3_vtab) .assert(c instanceof capi.sqlite3_vtab_cursor); wasm.setPtrValue(ppCursor, c.pointer); c._rowId = 0; return 0; }catch(e){ - return vth.xMethodError('xOpen',e); + return vth.xError('xOpen',e); } }, xClose: function(pCursor){ try{ - const c = vth.xWrapCursor(pCursor,true); + const c = vth.xCursor(pCursor,true); T.assert(c instanceof capi.sqlite3_vtab_cursor) - .assert(!vth.xWrapCursor(pCursor)); + .assert(!vth.xCursor(pCursor)); c.dispose(); return 0; }catch(e){ - return vth.xMethodError('xClose',e); + return vth.xError('xClose',e); } }, xNext: function(pCursor){ try{ - const c = vth.xWrapCursor(pCursor); + const c = vth.xCursor(pCursor); ++c._rowId; return 0; }catch(e){ - return vth.xMethodError('xNext',e); + return vth.xError('xNext',e); } }, xColumn: function(pCursor, pCtx, iCol){ try{ - const c = vth.xWrapCursor(pCursor); + const c = vth.xCursor(pCursor); switch(iCol){ case tmplCols.A: capi.sqlite3_result_int(pCtx, 1000 + c._rowId); @@ -1634,38 +1741,41 @@ self.sqlite3InitModule = sqlite3InitModule; } return 0; }catch(e){ - return vth.xMethodError('xColumn',e); + return vth.xError('xColumn',e); } }, xRowid: function(pCursor, ppRowid64){ try{ - const c = vth.xWrapCursor(pCursor); - vth.setRowId(ppRowid64, c._rowId); + const c = vth.xCursor(pCursor); + vth.xRowid(ppRowid64, c._rowId); return 0; }catch(e){ - return vth.xMethodError('xRowid',e); + return vth.xError('xRowid',e); } }, xEof: function(pCursor){ - const c = vth.xWrapCursor(pCursor); - return c._rowId>=10; + const c = vth.xCursor(pCursor), + rc = c._rowId>=10; + c.dispose(); + return rc; }, xFilter: function(pCursor, idxNum, idxCStr, argc, argv/* [sqlite3_value* ...] */){ try{ - const c = vth.xWrapCursor(pCursor); + const c = vth.xCursor(pCursor); c._rowId = 0; const list = vth.sqlite3ValuesToJs(argc, argv); T.assert(argc === list.length); //log(argc,"xFilter value(s):",list); + c.dispose(); return 0; }catch(e){ - return vth.xMethodError('xFilter',e); + return vth.xError('xFilter',e); } }, xBestIndex: function(pVtab, pIdxInfo){ try{ - //const t = vth.xWrapVtab(pVtab); + //const t = vth.xVtab(pVtab); const sii = capi.sqlite3_index_info; const pii = new sii(pIdxInfo); pii.$estimatedRows = 10; @@ -1706,7 +1816,7 @@ self.sqlite3InitModule = sqlite3InitModule; pii.dispose(); return 0; }catch(e){ - return vth.xMethodError('xBestIndex',e); + return vth.xError('xBestIndex',e); } } }; @@ -1727,8 +1837,8 @@ self.sqlite3InitModule = sqlite3InitModule; } const tmplMod = new sqlite3.capi.sqlite3_module(); - tmplMod.ondispose = []; tmplMod.$iVersion = 0; + this.db.onclose.disposeThese.push(tmplMod); vth.installMethods(tmplMod, tmplMethods, true); if(tmplMethods.xCreate){ T.assert(tmplMod.$xCreate) @@ -1750,94 +1860,165 @@ self.sqlite3InitModule = sqlite3InitModule; .assert(1000===list[0][0]) .assert(2009===list[list.length-1][1]) } - })/*vtab sanity checks*/ + })/*custom vtab #1*/ - //////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// .t({ - name: 'C-side WASM tests', - predicate: ()=>(haveWasmCTests() || "Not compiled in."), - test: function(){ - const w = wasm, db = this.db; - const stack = w.scopedAllocPush(); - let ptrInt; - const origValue = 512; - const ptrValType = 'i32'; - try{ - ptrInt = w.scopedAlloc(4); - w.setMemValue(ptrInt,origValue, ptrValType); - const cf = w.xGet('sqlite3_wasm_test_intptr'); - const oldPtrInt = ptrInt; - //log('ptrInt',ptrInt); - //log('getMemValue(ptrInt)',w.getMemValue(ptrInt)); - T.assert(origValue === w.getMemValue(ptrInt, ptrValType)); - const rc = cf(ptrInt); - //log('cf(ptrInt)',rc); - //log('ptrInt',ptrInt); - //log('getMemValue(ptrInt)',w.getMemValue(ptrInt,ptrValType)); - T.assert(2*origValue === rc). - assert(rc === w.getMemValue(ptrInt,ptrValType)). - assert(oldPtrInt === ptrInt); - const pi64 = w.scopedAlloc(8)/*ptr to 64-bit integer*/; - const o64 = 0x010203040506/*>32-bit integer*/; - const ptrType64 = 'i64'; - if(w.bigIntEnabled){ - w.setMemValue(pi64, o64, ptrType64); - //log("pi64 =",pi64, "o64 = 0x",o64.toString(16), o64); - const v64 = ()=>w.getMemValue(pi64,ptrType64) - //log("getMemValue(pi64)",v64()); - T.assert(v64() == o64); - //T.assert(o64 === w.getMemValue(pi64, ptrType64)); - const cf64w = w.xGet('sqlite3_wasm_test_int64ptr'); - cf64w(pi64); - //log("getMemValue(pi64)",v64()); - T.assert(v64() == BigInt(2 * o64)); - cf64w(pi64); - T.assert(v64() == BigInt(4 * o64)); - - const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2'); - T.assert(BigInt(2 * o64) === - biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError - in the call :/ */)); - - const pMin = w.scopedAlloc(16); - const pMax = pMin + 8; - const g64 = (p)=>w.getMemValue(p,ptrType64); - w.setMemValue(pMin, 0, ptrType64); - w.setMemValue(pMax, 0, ptrType64); - const minMaxI64 = [ - w.xCall('sqlite3_wasm_test_int64_min'), - w.xCall('sqlite3_wasm_test_int64_max') - ]; - T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)). - assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER)); - //log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]); - w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax); - T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch"). - assert(g64(pMax) === minMaxI64[1], "int64 mismatch"); - //log("pMin",g64(pMin), "pMax",g64(pMax)); - w.setMemValue(pMin, minMaxI64[0], ptrType64); - T.assert(g64(pMin) === minMaxI64[0]). - assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). - assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); - const rxRange = /too big/; - T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))}, - rxRange). - mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))}, - (e)=>rxRange.test(e.message)); + name: 'virtual table #2 (w/ automated exception wrapping)', + predicate: ()=>!!capi.sqlite3_index_info, + test: function(sqlite3){ + warn("The vtab/module JS bindings are experimental and subject to change."); + const vth = sqlite3.VtabHelper; + const tmplCols = Object.assign(Object.create(null),{ + A: 0, B: 1 + }); + /** + The vtab demonstrated here is a JS-ification of + ext/misc/templatevtab.c. + */ + let throwOnConnect = 1 ? 0 : capi.SQLITE_CANTOPEN + /* ^^^ just for testing exception wrapping. Note that sqlite + always translates errors from a vtable to a generic + SQLITE_ERROR unless it's from xConnect()/xCreate() and that + callback sets an error string. */; + const modConfig = { + /* catchExceptions changes how the methods are wrapped */ + catchExceptions: false, + name: "vtab2test", + methods:{ + xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){ + if(throwOnConnect){ + sqlite3.SQLite3Error.toss( + throwOnConnect, + "Throwing a test exception." + ); + } + const args = wasm.cArgvToJs(argc, argv); + console.debug("xCreate/xConnect args:",args); + T.assert(args.length>=3); + const rc = capi.sqlite3_declare_vtab( + pDb, "CREATE TABLE ignored(a,b)" + ); + if(0===rc){ + const t = vth.xVtab(); + wasm.setPtrValue(ppVtab, t.pointer); + T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab))); + } + return rc; + }, + xDisconnect: function(pVtab){ + const t = vth.xVtab(pVtab, true); + t.dispose(); + }, + xOpen: function(pVtab, ppCursor){ + const t = vth.xVtab(pVtab), c = vth.xCursor(); + T.assert(t instanceof capi.sqlite3_vtab) + .assert(c instanceof capi.sqlite3_vtab_cursor); + wasm.setPtrValue(ppCursor, c.pointer); + c._rowId = 0; + }, + xClose: function(pCursor){ + const c = vth.xCursor(pCursor,true); + T.assert(c instanceof capi.sqlite3_vtab_cursor) + .assert(!vth.xCursor(pCursor)); + c.dispose(); + }, + xNext: function(pCursor){ + const c = vth.xCursor(pCursor); + ++c._rowId; + }, + xColumn: function(pCursor, pCtx, iCol){ + const c = vth.xCursor(pCursor); + switch(iCol){ + case tmplCols.A: + capi.sqlite3_result_int(pCtx, 1000 + c._rowId); + break; + case tmplCols.B: + capi.sqlite3_result_int(pCtx, 2000 + c._rowId); + break; + default: sqlite3.SQLite3Error.toss("Invalid column id",iCol); + } + }, + xRowid: function(pCursor, ppRowid64){ + const c = vth.xCursor(pCursor); + vth.xRowid(ppRowid64, c._rowId); + c.dispose(); + }, + xEof: function(pCursor){ + const c = vth.xCursor(pCursor), + rc = c._rowId>=10; + c.dispose(); + return rc; + }, + xFilter: function(pCursor, idxNum, idxCStr, + argc, argv/* [sqlite3_value* ...] */){ + const c = vth.xCursor(pCursor); + c._rowId = 0; + const list = vth.sqlite3ValuesToJs(argc, argv); + T.assert(argc === list.length); + c.dispose(); + }, + xBestIndex: function(pVtab, pIdxInfo){ + //const t = vth.xVtab(pVtab); + const pii = vth.xIndexInfo(pIdxInfo); + pii.$estimatedRows = 10; + pii.$estimatedCost = 10.0; + pii.dispose(); + } + }/*methods*/ + }; + const doEponymous = + /* Bug (somewhere): non-eponymous is behaving as is + the call to sqlite3_create_module() is missing + or failed: + + SQL TRACE #63 create virtual table testvtab2 using vtab2test(arg1, arg2) + + => sqlite3 result code 1: no such module: vtab2test + */ true; + if(doEponymous){ + warn("Reminder: non-eponymous mode is still not working here.", + "Details are in the code comments."); + modConfig.methods.xCreate = 0; + }else{ + modConfig.methods.xCreate = (...args)=>0; + } + const tmplMod = vth.setupModule(modConfig); + T.assert(tmplMod instanceof capi.sqlite3_module) + .assert(1===tmplMod.$iVersion); + if(doEponymous){ + if(modConfig.methods.xCreate !== 0){ + T.assert(modConfig.methods.xCreate === modConfig.methods.xConnect) + .assert(tmplMod.$xCreate === tmplMod.$xConnect); }else{ - log("No BigInt support. Skipping related tests."); - log("\"The problem\" here is that we can manipulate, at the byte level,", - "heap memory to set 64-bit values, but we can't get those values", - "back into JS because of the lack of 64-bit integer support."); + T.assert(0 === tmplMod.$xCreate); } - }finally{ - const x = w.scopedAlloc(1), y = w.scopedAlloc(1), z = w.scopedAlloc(1); - //log("x=",x,"y=",y,"z=",z); // just looking at the alignment - w.scopedAllocPop(stack); } + this.db.onclose.disposeThese.push(tmplMod); + this.db.checkRc(capi.sqlite3_create_module( + this.db, modConfig.name, tmplMod, 0 + )); + if(!doEponymous){ + this.db.exec([ + "create virtual table testvtab2 using ", + modConfig.name, + "(arg1, arg2)" + ]); + } + const list = this.db.selectArrays( + ["SELECT a,b FROM ", + (doEponymous ? modConfig.name : "testvtab2"), + " where a<9999 and b>1 order by a, b" + ]/* Query is shaped so that it will ensure that some + constraints end up in xBestIndex(). */ + ); + T.assert(10===list.length) + .assert(1000===list[0][0]) + .assert(2009===list[list.length-1][1]) } - }/* jaccwabyt-specific tests */) + })/*custom vtab #2*/ + //////////////////////////////////////////////////////////////////////// .t('Close db', function(){ T.assert(this.db).assert(wasm.isPtr(this.db.pointer)); wasm.sqlite3_wasm_db_reset(this.db); |