koffi 2.2.0 → 2.2.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog.md +6 -0
- package/package.json +2 -2
- package/src/koffi/build/2.2.2-beta.1/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.2-beta.1/koffi_win32_x64.tar.gz +0 -0
- package/src/koffi/qemu/qemu.js +12 -10
- package/src/koffi/src/abi_arm32.cc +7 -11
- package/src/koffi/src/abi_arm64.cc +7 -11
- package/src/koffi/src/abi_riscv64.cc +7 -11
- package/src/koffi/src/abi_x64_sysv.cc +7 -11
- package/src/koffi/src/abi_x64_win.cc +7 -11
- package/src/koffi/src/abi_x86.cc +7 -11
- package/src/koffi/src/call.cc +53 -2
- package/src/koffi/src/call.hh +6 -1
- package/src/koffi/src/ffi.cc +66 -12
- package/src/koffi/src/ffi.hh +2 -0
- package/src/koffi/src/index.js +6 -2
- package/src/koffi/test/CMakeLists.txt +1 -1
- package/src/koffi/test/async.js +3 -0
- package/src/koffi/test/callbacks.js +30 -0
- package/src/koffi/test/misc.c +11 -0
- package/vendor/{sqlite3mc → sqlite3}/sqlite3.c +139 -43
- package/vendor/{sqlite3mc → sqlite3}/sqlite3.h +10 -3
- package/vendor/{sqlite3mc → sqlite3}/sqlite3ext.h +0 -0
- package/vendor/{sqlite3mc → sqlite3}/sqlite3mc.c +156 -53
- package/vendor/{sqlite3mc → sqlite3}/sqlite3mc.def +0 -0
- package/vendor/{sqlite3mc → sqlite3}/sqlite3mc.h +12 -5
- package/vendor/sqlite3/wasm/README.txt +23 -0
- package/vendor/sqlite3/wasm/common/SqliteTestUtil.js +236 -0
- package/vendor/sqlite3/wasm/common/emscripten.css +24 -0
- package/vendor/sqlite3/wasm/common/testing.css +63 -0
- package/vendor/sqlite3/wasm/demo-123-worker.html +44 -0
- package/vendor/sqlite3/wasm/demo-123.html +24 -0
- package/vendor/sqlite3/wasm/demo-123.js +289 -0
- package/vendor/sqlite3/wasm/demo-jsstorage.html +49 -0
- package/vendor/sqlite3/wasm/demo-jsstorage.js +114 -0
- package/vendor/sqlite3/wasm/demo-worker1-promiser.html +34 -0
- package/vendor/sqlite3/wasm/demo-worker1-promiser.js +270 -0
- package/vendor/sqlite3/wasm/demo-worker1.html +34 -0
- package/vendor/sqlite3/wasm/demo-worker1.js +345 -0
- package/vendor/sqlite3/wasm/index.html +90 -0
- package/vendor/sqlite3/wasm/jswasm/sqlite3-opfs-async-proxy.js +830 -0
- package/vendor/sqlite3/wasm/jswasm/sqlite3-worker1-promiser.js +259 -0
- package/vendor/sqlite3/wasm/jswasm/sqlite3-worker1.js +49 -0
- package/vendor/sqlite3/wasm/jswasm/sqlite3.js +10119 -0
- package/vendor/sqlite3/wasm/jswasm/sqlite3.wasm +0 -0
- package/vendor/sqlite3/wasm/tester1-worker.html +63 -0
- package/vendor/sqlite3/wasm/tester1.html +28 -0
- package/vendor/sqlite3/wasm/tester1.js +1864 -0
- package/src/koffi/build/2.2.0/koffi_darwin_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_darwin_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_freebsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_arm32hf.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_riscv64hf64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_linux_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_openbsd_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_openbsd_x64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_arm64.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_ia32.tar.gz +0 -0
- package/src/koffi/build/2.2.0/koffi_win32_x64.tar.gz +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/*
|
|
2
|
+
2022-05-22
|
|
3
|
+
|
|
4
|
+
The author disclaims copyright to this source code. In place of a
|
|
5
|
+
legal notice, here is a blessing:
|
|
6
|
+
|
|
7
|
+
* May you do good and not evil.
|
|
8
|
+
* May you find forgiveness for yourself and forgive others.
|
|
9
|
+
* May you share freely, never taking more than you give.
|
|
10
|
+
|
|
11
|
+
***********************************************************************
|
|
12
|
+
|
|
13
|
+
This file contains bootstrapping code used by various test scripts
|
|
14
|
+
which live in this file's directory.
|
|
15
|
+
*/
|
|
16
|
+
'use strict';
|
|
17
|
+
(function(self){
|
|
18
|
+
/* querySelectorAll() proxy */
|
|
19
|
+
const EAll = function(/*[element=document,] cssSelector*/){
|
|
20
|
+
return (arguments.length>1 ? arguments[0] : document)
|
|
21
|
+
.querySelectorAll(arguments[arguments.length-1]);
|
|
22
|
+
};
|
|
23
|
+
/* querySelector() proxy */
|
|
24
|
+
const E = function(/*[element=document,] cssSelector*/){
|
|
25
|
+
return (arguments.length>1 ? arguments[0] : document)
|
|
26
|
+
.querySelector(arguments[arguments.length-1]);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
Helpers for writing sqlite3-specific tests.
|
|
31
|
+
*/
|
|
32
|
+
self.SqliteTestUtil = {
|
|
33
|
+
/** Running total of the number of tests run via
|
|
34
|
+
this API. */
|
|
35
|
+
counter: 0,
|
|
36
|
+
/**
|
|
37
|
+
If expr is a function, it is called and its result
|
|
38
|
+
is returned, coerced to a bool, else expr, coerced to
|
|
39
|
+
a bool, is returned.
|
|
40
|
+
*/
|
|
41
|
+
toBool: function(expr){
|
|
42
|
+
return (expr instanceof Function) ? !!expr() : !!expr;
|
|
43
|
+
},
|
|
44
|
+
/** abort() if expr is false. If expr is a function, it
|
|
45
|
+
is called and its result is evaluated.
|
|
46
|
+
*/
|
|
47
|
+
assert: function f(expr, msg){
|
|
48
|
+
if(!f._){
|
|
49
|
+
f._ = ('undefined'===typeof abort
|
|
50
|
+
? (msg)=>{throw new Error(msg)}
|
|
51
|
+
: abort);
|
|
52
|
+
}
|
|
53
|
+
++this.counter;
|
|
54
|
+
if(!this.toBool(expr)){
|
|
55
|
+
f._(msg || "Assertion failed.");
|
|
56
|
+
}
|
|
57
|
+
return this;
|
|
58
|
+
},
|
|
59
|
+
/** Identical to assert() but throws instead of calling
|
|
60
|
+
abort(). */
|
|
61
|
+
affirm: function(expr, msg){
|
|
62
|
+
++this.counter;
|
|
63
|
+
if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed.");
|
|
64
|
+
return this;
|
|
65
|
+
},
|
|
66
|
+
/** Calls f() and squelches any exception it throws. If it
|
|
67
|
+
does not throw, this function throws. */
|
|
68
|
+
mustThrow: function(f, msg){
|
|
69
|
+
++this.counter;
|
|
70
|
+
let err;
|
|
71
|
+
try{ f(); } catch(e){err=e;}
|
|
72
|
+
if(!err) throw new Error(msg || "Expected exception.");
|
|
73
|
+
return this;
|
|
74
|
+
},
|
|
75
|
+
/**
|
|
76
|
+
Works like mustThrow() but expects filter to be a regex,
|
|
77
|
+
function, or string to match/filter the resulting exception
|
|
78
|
+
against. If f() does not throw, this test fails and an Error is
|
|
79
|
+
thrown. If filter is a regex, the test passes if
|
|
80
|
+
filter.test(error.message) passes. If it's a function, the test
|
|
81
|
+
passes if filter(error) returns truthy. If it's a string, the
|
|
82
|
+
test passes if the filter matches the exception message
|
|
83
|
+
precisely. In all other cases the test fails, throwing an
|
|
84
|
+
Error.
|
|
85
|
+
|
|
86
|
+
If it throws, msg is used as the error report unless it's falsy,
|
|
87
|
+
in which case a default is used.
|
|
88
|
+
*/
|
|
89
|
+
mustThrowMatching: function(f, filter, msg){
|
|
90
|
+
++this.counter;
|
|
91
|
+
let err;
|
|
92
|
+
try{ f(); } catch(e){err=e;}
|
|
93
|
+
if(!err) throw new Error(msg || "Expected exception.");
|
|
94
|
+
let pass = false;
|
|
95
|
+
if(filter instanceof RegExp) pass = filter.test(err.message);
|
|
96
|
+
else if(filter instanceof Function) pass = filter(err);
|
|
97
|
+
else if('string' === typeof filter) pass = (err.message === filter);
|
|
98
|
+
if(!pass){
|
|
99
|
+
throw new Error(msg || ("Filter rejected this exception: "+err.message));
|
|
100
|
+
}
|
|
101
|
+
return this;
|
|
102
|
+
},
|
|
103
|
+
/** Throws if expr is truthy or expr is a function and expr()
|
|
104
|
+
returns truthy. */
|
|
105
|
+
throwIf: function(expr, msg){
|
|
106
|
+
++this.counter;
|
|
107
|
+
if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
|
|
108
|
+
return this;
|
|
109
|
+
},
|
|
110
|
+
/** Throws if expr is falsy or expr is a function and expr()
|
|
111
|
+
returns falsy. */
|
|
112
|
+
throwUnless: function(expr, msg){
|
|
113
|
+
++this.counter;
|
|
114
|
+
if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
|
|
115
|
+
return this;
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
Parses window.location.search-style string into an object
|
|
120
|
+
containing key/value pairs of URL arguments (already
|
|
121
|
+
urldecoded). The object is created using Object.create(null),
|
|
122
|
+
so contains only parsed-out properties and has no prototype
|
|
123
|
+
(and thus no inherited properties).
|
|
124
|
+
|
|
125
|
+
If the str argument is not passed (arguments.length==0) then
|
|
126
|
+
window.location.search.substring(1) is used by default. If
|
|
127
|
+
neither str is passed in nor window exists then false is returned.
|
|
128
|
+
|
|
129
|
+
On success it returns an Object containing the key/value pairs
|
|
130
|
+
parsed from the string. Keys which have no value are treated
|
|
131
|
+
has having the boolean true value.
|
|
132
|
+
|
|
133
|
+
Pedantic licensing note: this code has appeared in other source
|
|
134
|
+
trees, but was originally written by the same person who pasted
|
|
135
|
+
it into those trees.
|
|
136
|
+
*/
|
|
137
|
+
processUrlArgs: function(str) {
|
|
138
|
+
if( 0 === arguments.length ) {
|
|
139
|
+
if( ('undefined' === typeof window) ||
|
|
140
|
+
!window.location ||
|
|
141
|
+
!window.location.search ) return false;
|
|
142
|
+
else str = (''+window.location.search).substring(1);
|
|
143
|
+
}
|
|
144
|
+
if( ! str ) return false;
|
|
145
|
+
str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
|
|
146
|
+
const args = Object.create(null);
|
|
147
|
+
const sp = str.split(/&+/);
|
|
148
|
+
const rx = /^([^=]+)(=(.+))?/;
|
|
149
|
+
var i, m;
|
|
150
|
+
for( i in sp ) {
|
|
151
|
+
m = rx.exec( sp[i] );
|
|
152
|
+
if( ! m ) continue;
|
|
153
|
+
args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
|
|
154
|
+
}
|
|
155
|
+
return args;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
This is a module object for use with the emscripten-installed
|
|
162
|
+
sqlite3InitModule() factory function.
|
|
163
|
+
*/
|
|
164
|
+
self.sqlite3TestModule = {
|
|
165
|
+
/**
|
|
166
|
+
Array of functions to call after Emscripten has initialized the
|
|
167
|
+
wasm module. Each gets passed the Emscripten module object
|
|
168
|
+
(which is _this_ object).
|
|
169
|
+
*/
|
|
170
|
+
postRun: [
|
|
171
|
+
/* function(theModule){...} */
|
|
172
|
+
],
|
|
173
|
+
//onRuntimeInitialized: function(){},
|
|
174
|
+
/* Proxy for C-side stdout output. */
|
|
175
|
+
print: (...args)=>{console.log(...args)},
|
|
176
|
+
/* Proxy for C-side stderr output. */
|
|
177
|
+
printErr: (...args)=>{console.error(...args)},
|
|
178
|
+
/**
|
|
179
|
+
Called by the Emscripten module init bits to report loading
|
|
180
|
+
progress. It gets passed an empty argument when loading is done
|
|
181
|
+
(after onRuntimeInitialized() and any this.postRun callbacks
|
|
182
|
+
have been run).
|
|
183
|
+
*/
|
|
184
|
+
setStatus: function f(text){
|
|
185
|
+
if(!f.last){
|
|
186
|
+
f.last = { text: '', step: 0 };
|
|
187
|
+
f.ui = {
|
|
188
|
+
status: E('#module-status'),
|
|
189
|
+
progress: E('#module-progress'),
|
|
190
|
+
spinner: E('#module-spinner')
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if(text === f.last.text) return;
|
|
194
|
+
f.last.text = text;
|
|
195
|
+
if(f.ui.progress){
|
|
196
|
+
f.ui.progress.value = f.last.step;
|
|
197
|
+
f.ui.progress.max = f.last.step + 1;
|
|
198
|
+
}
|
|
199
|
+
++f.last.step;
|
|
200
|
+
if(text) {
|
|
201
|
+
f.ui.status.classList.remove('hidden');
|
|
202
|
+
f.ui.status.innerText = text;
|
|
203
|
+
}else{
|
|
204
|
+
if(f.ui.progress){
|
|
205
|
+
f.ui.progress.remove();
|
|
206
|
+
f.ui.spinner.remove();
|
|
207
|
+
delete f.ui.progress;
|
|
208
|
+
delete f.ui.spinner;
|
|
209
|
+
}
|
|
210
|
+
f.ui.status.classList.add('hidden');
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
/**
|
|
214
|
+
Config options used by the Emscripten-dependent initialization
|
|
215
|
+
which happens via this.initSqlite3(). This object gets
|
|
216
|
+
(indirectly) passed to sqlite3ApiBootstrap() to configure the
|
|
217
|
+
sqlite3 API.
|
|
218
|
+
*/
|
|
219
|
+
sqlite3ApiConfig: {
|
|
220
|
+
wasmfsOpfsDir: "/opfs"
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
Intended to be called by apps which need to call the
|
|
224
|
+
Emscripten-installed sqlite3InitModule() routine. This function
|
|
225
|
+
temporarily installs this.sqlite3ApiConfig into the self
|
|
226
|
+
object, calls it sqlite3InitModule(), and removes
|
|
227
|
+
self.sqlite3ApiConfig after initialization is done. Returns the
|
|
228
|
+
promise from sqlite3InitModule(), and the next then() handler
|
|
229
|
+
will get the sqlite3 API object as its argument.
|
|
230
|
+
*/
|
|
231
|
+
initSqlite3: function(){
|
|
232
|
+
self.sqlite3ApiConfig = this.sqlite3ApiConfig;
|
|
233
|
+
return self.sqlite3InitModule(this).finally(()=>delete self.sqlite3ApiConfig);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
})(self/*window or worker*/);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* emcscript-related styling, used during the module load/intialization processes... */
|
|
2
|
+
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
|
3
|
+
div.emscripten { text-align: center; }
|
|
4
|
+
div.emscripten_border { border: 1px solid black; }
|
|
5
|
+
#module-spinner { overflow: visible; }
|
|
6
|
+
#module-spinner > * {
|
|
7
|
+
margin-top: 1em;
|
|
8
|
+
}
|
|
9
|
+
.spinner {
|
|
10
|
+
height: 50px;
|
|
11
|
+
width: 50px;
|
|
12
|
+
margin: 0px auto;
|
|
13
|
+
animation: rotation 0.8s linear infinite;
|
|
14
|
+
border-left: 10px solid rgb(0,150,240);
|
|
15
|
+
border-right: 10px solid rgb(0,150,240);
|
|
16
|
+
border-bottom: 10px solid rgb(0,150,240);
|
|
17
|
+
border-top: 10px solid rgb(100,0,200);
|
|
18
|
+
border-radius: 100%;
|
|
19
|
+
background-color: rgb(200,100,250);
|
|
20
|
+
}
|
|
21
|
+
@keyframes rotation {
|
|
22
|
+
from {transform: rotate(0deg);}
|
|
23
|
+
to {transform: rotate(360deg);}
|
|
24
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
body {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
flex-wrap: wrap;
|
|
5
|
+
}
|
|
6
|
+
textarea {
|
|
7
|
+
font-family: monospace;
|
|
8
|
+
}
|
|
9
|
+
header {
|
|
10
|
+
font-size: 130%;
|
|
11
|
+
font-weight: bold;
|
|
12
|
+
}
|
|
13
|
+
.hidden, .initially-hidden {
|
|
14
|
+
position: absolute !important;
|
|
15
|
+
opacity: 0 !important;
|
|
16
|
+
pointer-events: none !important;
|
|
17
|
+
display: none !important;
|
|
18
|
+
}
|
|
19
|
+
fieldset.options {
|
|
20
|
+
font-size: 75%;
|
|
21
|
+
}
|
|
22
|
+
fieldset > legend {
|
|
23
|
+
padding: 0 0.5em;
|
|
24
|
+
}
|
|
25
|
+
span.labeled-input {
|
|
26
|
+
padding: 0.25em;
|
|
27
|
+
margin: 0.25em 0.5em;
|
|
28
|
+
border-radius: 0.25em;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
background: #0002;
|
|
31
|
+
}
|
|
32
|
+
.center { text-align: center; }
|
|
33
|
+
.error {
|
|
34
|
+
color: red;
|
|
35
|
+
background-color: yellow;
|
|
36
|
+
}
|
|
37
|
+
.strong { font-weight: 700 }
|
|
38
|
+
.warning { color: firebrick; }
|
|
39
|
+
.green { color: darkgreen; }
|
|
40
|
+
.tests-pass { background-color: green; color: white }
|
|
41
|
+
.tests-fail { background-color: red; color: yellow }
|
|
42
|
+
.faded { opacity: 0.5; }
|
|
43
|
+
.group-start { color: blue; }
|
|
44
|
+
.group-end { color: blue; }
|
|
45
|
+
.input-wrapper {
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
}
|
|
50
|
+
#test-output {
|
|
51
|
+
border: 1px inset;
|
|
52
|
+
border-radius: 0.25em;
|
|
53
|
+
padding: 0.25em;
|
|
54
|
+
/*max-height: 30em;*/
|
|
55
|
+
overflow: auto;
|
|
56
|
+
white-space: break-spaces;
|
|
57
|
+
display: flex; flex-direction: column;
|
|
58
|
+
font-family: monospace;
|
|
59
|
+
}
|
|
60
|
+
#test-output.reverse {
|
|
61
|
+
flex-direction: column-reverse;
|
|
62
|
+
}
|
|
63
|
+
label[for] { cursor: pointer }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en-us">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
6
|
+
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
|
7
|
+
<title>Hello, sqlite3</title>
|
|
8
|
+
<style>
|
|
9
|
+
.warning, .error {color: red}
|
|
10
|
+
.error {background-color: yellow}
|
|
11
|
+
body {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
font-family: monospace;
|
|
15
|
+
white-space: break-spaces;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<h1>1-2-sqlite3 worker demo</h1>
|
|
21
|
+
<script>(function(){
|
|
22
|
+
const logHtml = function(cssClass,...args){
|
|
23
|
+
const ln = document.createElement('div');
|
|
24
|
+
if(cssClass) ln.classList.add(cssClass);
|
|
25
|
+
ln.append(document.createTextNode(args.join(' ')));
|
|
26
|
+
document.body.append(ln);
|
|
27
|
+
};
|
|
28
|
+
const w = new Worker("demo-123.js?sqlite3.dir=jswasm"
|
|
29
|
+
/* Note the URL argument on that name. See
|
|
30
|
+
the notes in demo-123.js (search for
|
|
31
|
+
"importScripts") for why we need
|
|
32
|
+
that. */);
|
|
33
|
+
w.onmessage = function({data}){
|
|
34
|
+
switch(data.type){
|
|
35
|
+
case 'log':
|
|
36
|
+
logHtml(data.payload.cssClass, ...data.payload.args);
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
logHtml('error',"Unhandled message:",data.type);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
})();</script>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en-us">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
6
|
+
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
|
7
|
+
<title>Hello, sqlite3</title>
|
|
8
|
+
<style>
|
|
9
|
+
.warning, .error {color: red}
|
|
10
|
+
.error {background-color: yellow}
|
|
11
|
+
body {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
font-family: monospace;
|
|
15
|
+
white-space: break-spaces;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<h1>1-2-sqlite3 demo</h1>
|
|
21
|
+
<script src="jswasm/sqlite3.js"></script>
|
|
22
|
+
<script src="demo-123.js"></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/*
|
|
2
|
+
2022-09-19
|
|
3
|
+
|
|
4
|
+
The author disclaims copyright to this source code. In place of a
|
|
5
|
+
legal notice, here is a blessing:
|
|
6
|
+
|
|
7
|
+
* May you do good and not evil.
|
|
8
|
+
* May you find forgiveness for yourself and forgive others.
|
|
9
|
+
* May you share freely, never taking more than you give.
|
|
10
|
+
|
|
11
|
+
***********************************************************************
|
|
12
|
+
|
|
13
|
+
A basic demonstration of the SQLite3 "OO#1" API.
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
(function(){
|
|
17
|
+
/**
|
|
18
|
+
Set up our output channel differently depending
|
|
19
|
+
on whether we are running in a worker thread or
|
|
20
|
+
the main (UI) thread.
|
|
21
|
+
*/
|
|
22
|
+
let logHtml;
|
|
23
|
+
if(self.window === self /* UI thread */){
|
|
24
|
+
console.log("Running demo from main UI thread.");
|
|
25
|
+
logHtml = function(cssClass,...args){
|
|
26
|
+
const ln = document.createElement('div');
|
|
27
|
+
if(cssClass) ln.classList.add(cssClass);
|
|
28
|
+
ln.append(document.createTextNode(args.join(' ')));
|
|
29
|
+
document.body.append(ln);
|
|
30
|
+
};
|
|
31
|
+
}else{ /* Worker thread */
|
|
32
|
+
console.log("Running demo from Worker thread.");
|
|
33
|
+
logHtml = function(cssClass,...args){
|
|
34
|
+
postMessage({
|
|
35
|
+
type:'log',
|
|
36
|
+
payload:{cssClass, args}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const log = (...args)=>logHtml('',...args);
|
|
41
|
+
const warn = (...args)=>logHtml('warning',...args);
|
|
42
|
+
const error = (...args)=>logHtml('error',...args);
|
|
43
|
+
|
|
44
|
+
const demo1 = function(sqlite3){
|
|
45
|
+
const capi = sqlite3.capi/*C-style API*/,
|
|
46
|
+
oo = sqlite3.oo1/*high-level OO API*/;
|
|
47
|
+
log("sqlite3 version",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
|
|
48
|
+
const db = new oo.DB("/mydb.sqlite3",'ct');
|
|
49
|
+
log("transient db =",db.filename);
|
|
50
|
+
/**
|
|
51
|
+
Never(!) rely on garbage collection to clean up DBs and
|
|
52
|
+
(especially) prepared statements. Always wrap their lifetimes
|
|
53
|
+
in a try/finally construct, as demonstrated below. By and
|
|
54
|
+
large, client code can entirely avoid lifetime-related
|
|
55
|
+
complications of prepared statement objects by using the
|
|
56
|
+
DB.exec() method for SQL execution.
|
|
57
|
+
*/
|
|
58
|
+
try {
|
|
59
|
+
log("Create a table...");
|
|
60
|
+
db.exec("CREATE TABLE IF NOT EXISTS t(a,b)");
|
|
61
|
+
//Equivalent:
|
|
62
|
+
db.exec({
|
|
63
|
+
sql:"CREATE TABLE IF NOT EXISTS t(a,b)"
|
|
64
|
+
// ... numerous other options ...
|
|
65
|
+
});
|
|
66
|
+
// SQL can be either a string or a byte array
|
|
67
|
+
// or an array of strings which get concatenated
|
|
68
|
+
// together as-is (so be sure to end each statement
|
|
69
|
+
// with a semicolon).
|
|
70
|
+
|
|
71
|
+
log("Insert some data using exec()...");
|
|
72
|
+
let i;
|
|
73
|
+
for( i = 20; i <= 25; ++i ){
|
|
74
|
+
db.exec({
|
|
75
|
+
sql: "insert into t(a,b) values (?,?)",
|
|
76
|
+
// bind by parameter index...
|
|
77
|
+
bind: [i, i*2]
|
|
78
|
+
});
|
|
79
|
+
db.exec({
|
|
80
|
+
sql: "insert into t(a,b) values ($a,$b)",
|
|
81
|
+
// bind by parameter name...
|
|
82
|
+
bind: {$a: i * 10, $b: i * 20}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
log("Insert using a prepared statement...");
|
|
87
|
+
let q = db.prepare([
|
|
88
|
+
// SQL may be a string or array of strings
|
|
89
|
+
// (concatenated w/o separators).
|
|
90
|
+
"insert into t(a,b) ",
|
|
91
|
+
"values(?,?)"
|
|
92
|
+
]);
|
|
93
|
+
try {
|
|
94
|
+
for( i = 100; i < 103; ++i ){
|
|
95
|
+
q.bind( [i, i*2] ).step();
|
|
96
|
+
q.reset();
|
|
97
|
+
}
|
|
98
|
+
// Equivalent...
|
|
99
|
+
for( i = 103; i <= 105; ++i ){
|
|
100
|
+
q.bind(1, i).bind(2, i*2).stepReset();
|
|
101
|
+
}
|
|
102
|
+
}finally{
|
|
103
|
+
q.finalize();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
log("Query data with exec() using rowMode 'array'...");
|
|
107
|
+
db.exec({
|
|
108
|
+
sql: "select a from t order by a limit 3",
|
|
109
|
+
rowMode: 'array', // 'array' (default), 'object', or 'stmt'
|
|
110
|
+
callback: function(row){
|
|
111
|
+
log("row ",++this.counter,"=",row);
|
|
112
|
+
}.bind({counter: 0})
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
log("Query data with exec() using rowMode 'object'...");
|
|
116
|
+
db.exec({
|
|
117
|
+
sql: "select a as aa, b as bb from t order by aa limit 3",
|
|
118
|
+
rowMode: 'object',
|
|
119
|
+
callback: function(row){
|
|
120
|
+
log("row ",++this.counter,"=",JSON.stringify(row));
|
|
121
|
+
}.bind({counter: 0})
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
log("Query data with exec() using rowMode 'stmt'...");
|
|
125
|
+
db.exec({
|
|
126
|
+
sql: "select a from t order by a limit 3",
|
|
127
|
+
rowMode: 'stmt',
|
|
128
|
+
callback: function(row){
|
|
129
|
+
log("row ",++this.counter,"get(0) =",row.get(0));
|
|
130
|
+
}.bind({counter: 0})
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
log("Query data with exec() using rowMode INTEGER (result column index)...");
|
|
134
|
+
db.exec({
|
|
135
|
+
sql: "select a, b from t order by a limit 3",
|
|
136
|
+
rowMode: 1, // === result column 1
|
|
137
|
+
callback: function(row){
|
|
138
|
+
log("row ",++this.counter,"b =",row);
|
|
139
|
+
}.bind({counter: 0})
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
log("Query data with exec() using rowMode $COLNAME (result column name)...");
|
|
143
|
+
db.exec({
|
|
144
|
+
sql: "select a a, b from t order by a limit 3",
|
|
145
|
+
rowMode: '$a',
|
|
146
|
+
callback: function(value){
|
|
147
|
+
log("row ",++this.counter,"a =",value);
|
|
148
|
+
}.bind({counter: 0})
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
log("Query data with exec() without a callback...");
|
|
152
|
+
let resultRows = [];
|
|
153
|
+
db.exec({
|
|
154
|
+
sql: "select a, b from t order by a limit 3",
|
|
155
|
+
rowMode: 'object',
|
|
156
|
+
resultRows: resultRows
|
|
157
|
+
});
|
|
158
|
+
log("Result rows:",JSON.stringify(resultRows,undefined,2));
|
|
159
|
+
|
|
160
|
+
log("Create a scalar UDF...");
|
|
161
|
+
db.createFunction({
|
|
162
|
+
name: 'twice',
|
|
163
|
+
xFunc: function(pCx, arg){ // note the call arg count
|
|
164
|
+
return arg + arg;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
log("Run scalar UDF and collect result column names...");
|
|
168
|
+
let columnNames = [];
|
|
169
|
+
db.exec({
|
|
170
|
+
sql: "select a, twice(a), twice(''||a) from t order by a desc limit 3",
|
|
171
|
+
columnNames: columnNames,
|
|
172
|
+
rowMode: 'stmt',
|
|
173
|
+
callback: function(row){
|
|
174
|
+
log("a =",row.get(0), "twice(a) =", row.get(1),
|
|
175
|
+
"twice(''||a) =",row.get(2));
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
log("Result column names:",columnNames);
|
|
179
|
+
|
|
180
|
+
try{
|
|
181
|
+
log("The following use of the twice() UDF will",
|
|
182
|
+
"fail because of incorrect arg count...");
|
|
183
|
+
db.exec("select twice(1,2,3)");
|
|
184
|
+
}catch(e){
|
|
185
|
+
warn("Got expected exception:",e.message);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
db.transaction( function(D) {
|
|
190
|
+
D.exec("delete from t");
|
|
191
|
+
log("In transaction: count(*) from t =",db.selectValue("select count(*) from t"));
|
|
192
|
+
throw new sqlite3.SQLite3Error("Demonstrating transaction() rollback");
|
|
193
|
+
});
|
|
194
|
+
}catch(e){
|
|
195
|
+
if(e instanceof sqlite3.SQLite3Error){
|
|
196
|
+
log("Got expected exception from db.transaction():",e.message);
|
|
197
|
+
log("count(*) from t =",db.selectValue("select count(*) from t"));
|
|
198
|
+
}else{
|
|
199
|
+
throw e;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
db.savepoint( function(D) {
|
|
205
|
+
D.exec("delete from t");
|
|
206
|
+
log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t"));
|
|
207
|
+
D.savepoint(function(DD){
|
|
208
|
+
const rows = [];
|
|
209
|
+
DD.exec({
|
|
210
|
+
sql: ["insert into t(a,b) values(99,100);",
|
|
211
|
+
"select count(*) from t"],
|
|
212
|
+
rowMode: 0,
|
|
213
|
+
resultRows: rows
|
|
214
|
+
});
|
|
215
|
+
log("In nested savepoint. Row count =",rows[0]);
|
|
216
|
+
throw new sqlite3.SQLite3Error("Demonstrating nested savepoint() rollback");
|
|
217
|
+
})
|
|
218
|
+
});
|
|
219
|
+
}catch(e){
|
|
220
|
+
if(e instanceof sqlite3.SQLite3Error){
|
|
221
|
+
log("Got expected exception from nested db.savepoint():",e.message);
|
|
222
|
+
log("count(*) from t =",db.selectValue("select count(*) from t"));
|
|
223
|
+
}else{
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}finally{
|
|
228
|
+
db.close();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
log("That's all, folks!");
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
Some of the features of the OO API not demonstrated above...
|
|
235
|
+
|
|
236
|
+
- get change count (total or statement-local, 32- or 64-bit)
|
|
237
|
+
- get a DB's file name
|
|
238
|
+
|
|
239
|
+
Misc. Stmt features:
|
|
240
|
+
|
|
241
|
+
- Various forms of bind()
|
|
242
|
+
- clearBindings()
|
|
243
|
+
- reset()
|
|
244
|
+
- Various forms of step()
|
|
245
|
+
- Variants of get() for explicit type treatment/conversion,
|
|
246
|
+
e.g. getInt(), getFloat(), getBlob(), getJSON()
|
|
247
|
+
- getColumnName(ndx), getColumnNames()
|
|
248
|
+
- getParamIndex(name)
|
|
249
|
+
*/
|
|
250
|
+
}/*demo1()*/;
|
|
251
|
+
|
|
252
|
+
log("Loading and initializing sqlite3 module...");
|
|
253
|
+
if(self.window!==self) /*worker thread*/{
|
|
254
|
+
/*
|
|
255
|
+
If sqlite3.js is in a directory other than this script, in order
|
|
256
|
+
to get sqlite3.js to resolve sqlite3.wasm properly, we have to
|
|
257
|
+
explicitly tell it where sqlite3.js is being loaded from. We do
|
|
258
|
+
that by passing the `sqlite3.dir=theDirName` URL argument to
|
|
259
|
+
_this_ script. That URL argument will be seen by the JS/WASM
|
|
260
|
+
loader and it will adjust the sqlite3.wasm path accordingly. If
|
|
261
|
+
sqlite3.js/.wasm are in the same directory as this script then
|
|
262
|
+
that's not needed.
|
|
263
|
+
|
|
264
|
+
URL arguments passed as part of the filename via importScripts()
|
|
265
|
+
are simply lost, and such scripts see the self.location of
|
|
266
|
+
_this_ script.
|
|
267
|
+
*/
|
|
268
|
+
let sqlite3Js = 'sqlite3.js';
|
|
269
|
+
const urlParams = new URL(self.location.href).searchParams;
|
|
270
|
+
if(urlParams.has('sqlite3.dir')){
|
|
271
|
+
sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
|
|
272
|
+
}
|
|
273
|
+
importScripts(sqlite3Js);
|
|
274
|
+
}
|
|
275
|
+
self.sqlite3InitModule({
|
|
276
|
+
// We can redirect any stdout/stderr from the module
|
|
277
|
+
// like so...
|
|
278
|
+
print: log,
|
|
279
|
+
printErr: error
|
|
280
|
+
}).then(function(sqlite3){
|
|
281
|
+
//console.log('sqlite3 =',sqlite3);
|
|
282
|
+
log("Done initializing. Running demo...");
|
|
283
|
+
try {
|
|
284
|
+
demo1(sqlite3);
|
|
285
|
+
}catch(e){
|
|
286
|
+
error("Exception:",e.message);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
})();
|