@livestore/wa-sqlite 1.0.1-dev.0
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/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/wa-sqlite-async.mjs +16 -0
- package/dist/wa-sqlite-async.wasm +0 -0
- package/dist/wa-sqlite-jspi.mjs +16 -0
- package/dist/wa-sqlite-jspi.wasm +0 -0
- package/dist/wa-sqlite.mjs +16 -0
- package/dist/wa-sqlite.wasm +0 -0
- package/package.json +45 -0
- package/src/FacadeVFS.js +508 -0
- package/src/VFS.js +222 -0
- package/src/WebLocksMixin.js +412 -0
- package/src/examples/AccessHandlePoolVFS.js +458 -0
- package/src/examples/IDBBatchAtomicVFS.js +820 -0
- package/src/examples/IDBMirrorVFS.js +875 -0
- package/src/examples/MemoryAsyncVFS.js +100 -0
- package/src/examples/MemoryVFS.js +176 -0
- package/src/examples/OPFSAdaptiveVFS.js +437 -0
- package/src/examples/OPFSAnyContextVFS.js +300 -0
- package/src/examples/OPFSCoopSyncVFS.js +590 -0
- package/src/examples/OPFSPermutedVFS.js +1214 -0
- package/src/examples/README.md +89 -0
- package/src/examples/tag.js +82 -0
- package/src/sqlite-api.js +914 -0
- package/src/sqlite-constants.js +275 -0
- package/src/types/globals.d.ts +60 -0
- package/src/types/index.d.ts +1302 -0
- package/src/types/tsconfig.json +6 -0
- package/test/AccessHandlePoolVFS.test.js +27 -0
- package/test/IDBBatchAtomicVFS.test.js +97 -0
- package/test/IDBMirrorVFS.test.js +27 -0
- package/test/MemoryAsyncVFS.test.js +27 -0
- package/test/MemoryVFS.test.js +27 -0
- package/test/OPFSAdaptiveVFS.test.js +27 -0
- package/test/OPFSAnyContextVFS.test.js +27 -0
- package/test/OPFSCoopSyncVFS.test.js +27 -0
- package/test/OPFSPermutedVFS.test.js +27 -0
- package/test/TestContext.js +96 -0
- package/test/WebLocksMixin.test.js +521 -0
- package/test/api.test.js +49 -0
- package/test/api_exec.js +89 -0
- package/test/api_misc.js +63 -0
- package/test/api_statements.js +426 -0
- package/test/callbacks.test.js +373 -0
- package/test/sql.test.js +64 -0
- package/test/sql_0001.js +49 -0
- package/test/sql_0002.js +52 -0
- package/test/sql_0003.js +83 -0
- package/test/sql_0004.js +81 -0
- package/test/sql_0005.js +76 -0
- package/test/test-worker.js +204 -0
- package/test/vfs_xAccess.js +2 -0
- package/test/vfs_xClose.js +52 -0
- package/test/vfs_xOpen.js +91 -0
- package/test/vfs_xRead.js +38 -0
- package/test/vfs_xWrite.js +36 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { TestContext } from "./TestContext.js";
|
|
2
|
+
import AsyncifyFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs';
|
|
3
|
+
import JSPIFactory from 'wa-sqlite/dist/wa-sqlite-jspi.mjs';
|
|
4
|
+
import * as SQLite from '../src/sqlite-api.js';
|
|
5
|
+
|
|
6
|
+
const FACTORIES = new Map([
|
|
7
|
+
['asyncify', AsyncifyFactory],
|
|
8
|
+
['jspi', JSPIFactory]
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
const supportsJSPI = await TestContext.supportsJSPI();
|
|
12
|
+
|
|
13
|
+
for (const [key, factory] of FACTORIES) {
|
|
14
|
+
if (key === 'jspi' && !supportsJSPI) continue;
|
|
15
|
+
|
|
16
|
+
const sqlite3 = await factory().then(module => SQLite.Factory(module));
|
|
17
|
+
describe(`${key} create_function`, function() {
|
|
18
|
+
let db;
|
|
19
|
+
beforeEach(async function() {
|
|
20
|
+
db = await sqlite3.open_v2(':memory:');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(async function() {
|
|
24
|
+
await sqlite3.close(db);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return an int', async function() {
|
|
28
|
+
let rc;
|
|
29
|
+
|
|
30
|
+
rc = await sqlite3.create_function(
|
|
31
|
+
db,
|
|
32
|
+
'fn',
|
|
33
|
+
0,
|
|
34
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
35
|
+
(function(context, values) {
|
|
36
|
+
sqlite3.result_int(context, 42);
|
|
37
|
+
}));
|
|
38
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
39
|
+
|
|
40
|
+
let result;
|
|
41
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
42
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
43
|
+
expect(result).toEqual(42);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return an int64', async function() {
|
|
47
|
+
let rc;
|
|
48
|
+
|
|
49
|
+
rc = await sqlite3.create_function(
|
|
50
|
+
db,
|
|
51
|
+
'fn',
|
|
52
|
+
0,
|
|
53
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
54
|
+
(function(context, values) {
|
|
55
|
+
sqlite3.result_int64(context, 0x7FFF_FFFF_FFFF_FFFFn);
|
|
56
|
+
}));
|
|
57
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
58
|
+
|
|
59
|
+
for await (const stmt of sqlite3.statements(db, 'SELECT fn()')) {
|
|
60
|
+
while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) {
|
|
61
|
+
const value = sqlite3.column_int64(stmt, 0);
|
|
62
|
+
expect(value).toEqual(0x7FFF_FFFF_FFFF_FFFFn);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should return a double', async function() {
|
|
68
|
+
let rc;
|
|
69
|
+
|
|
70
|
+
rc = await sqlite3.create_function(
|
|
71
|
+
db,
|
|
72
|
+
'fn',
|
|
73
|
+
0,
|
|
74
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
75
|
+
(function(context, values) {
|
|
76
|
+
sqlite3.result_double(context, 3.14);
|
|
77
|
+
}));
|
|
78
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
79
|
+
|
|
80
|
+
let result;
|
|
81
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
82
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
83
|
+
expect(result).toEqual(3.14);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return a string', async function() {
|
|
87
|
+
let rc;
|
|
88
|
+
|
|
89
|
+
rc = await sqlite3.create_function(
|
|
90
|
+
db,
|
|
91
|
+
'fn',
|
|
92
|
+
0,
|
|
93
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
94
|
+
(function(context, values) {
|
|
95
|
+
sqlite3.result_text(context, 'foobar');
|
|
96
|
+
}));
|
|
97
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
98
|
+
|
|
99
|
+
let result;
|
|
100
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
101
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
102
|
+
expect(result).toEqual('foobar');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should return a blob', async function() {
|
|
106
|
+
let rc;
|
|
107
|
+
|
|
108
|
+
rc = await sqlite3.create_function(
|
|
109
|
+
db,
|
|
110
|
+
'fn',
|
|
111
|
+
0,
|
|
112
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
113
|
+
(function(context, values) {
|
|
114
|
+
sqlite3.result_blob(context, new Uint8Array([0x12, 0x34, 0x56]));
|
|
115
|
+
}));
|
|
116
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
117
|
+
|
|
118
|
+
let result;
|
|
119
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
120
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
121
|
+
expect(result).toEqual(new Uint8Array([0x12, 0x34, 0x56]));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should return null', async function() {
|
|
125
|
+
let rc;
|
|
126
|
+
|
|
127
|
+
rc = await sqlite3.create_function(
|
|
128
|
+
db,
|
|
129
|
+
'fn',
|
|
130
|
+
0,
|
|
131
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
132
|
+
(function(context, values) {
|
|
133
|
+
sqlite3.result_null(context);
|
|
134
|
+
}));
|
|
135
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
136
|
+
|
|
137
|
+
let result;
|
|
138
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
139
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
140
|
+
expect(result).toEqual(null);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should pass a fixed number of arguments', async function() {
|
|
144
|
+
let rc;
|
|
145
|
+
|
|
146
|
+
rc = await sqlite3.create_function(
|
|
147
|
+
db,
|
|
148
|
+
'fn',
|
|
149
|
+
5,
|
|
150
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
151
|
+
(function(context, values) {
|
|
152
|
+
expect(sqlite3.value_type(values[0])).toEqual(SQLite.SQLITE_INTEGER);
|
|
153
|
+
expect(sqlite3.value_int(values[0])).toEqual(42);
|
|
154
|
+
expect(sqlite3.value_int64(values[0])).toEqual(42n);
|
|
155
|
+
expect(sqlite3.value(values[0])).toEqual(42);
|
|
156
|
+
|
|
157
|
+
expect(sqlite3.value_type(values[1])).toEqual(SQLite.SQLITE_FLOAT);
|
|
158
|
+
expect(sqlite3.value_double(values[1])).toEqual(3.14);
|
|
159
|
+
expect(sqlite3.value(values[1])).toEqual(3.14);
|
|
160
|
+
|
|
161
|
+
expect(sqlite3.value_type(values[2])).toEqual(SQLite.SQLITE_TEXT);
|
|
162
|
+
expect(sqlite3.value_text(values[2])).toEqual('hello');
|
|
163
|
+
expect(sqlite3.value(values[2])).toEqual('hello');
|
|
164
|
+
|
|
165
|
+
expect(sqlite3.value_type(values[3])).toEqual(SQLite.SQLITE_BLOB);
|
|
166
|
+
expect(sqlite3.value_blob(values[3])).toEqual(new Uint8Array([0x12, 0x34, 0x56]));
|
|
167
|
+
expect(sqlite3.value_bytes(values[3])).toEqual(3);
|
|
168
|
+
expect(sqlite3.value(values[3])).toEqual(new Uint8Array([0x12, 0x34, 0x56]));
|
|
169
|
+
|
|
170
|
+
expect(sqlite3.value_type(values[4])).toEqual(SQLite.SQLITE_NULL);
|
|
171
|
+
}));
|
|
172
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
173
|
+
|
|
174
|
+
rc = await sqlite3.exec(db, `
|
|
175
|
+
SELECT fn(42, 3.14, 'hello', x'123456', NULL)
|
|
176
|
+
`);
|
|
177
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should pass a variable number of arguments', async function() {
|
|
181
|
+
let rc;
|
|
182
|
+
|
|
183
|
+
rc = await sqlite3.create_function(
|
|
184
|
+
db,
|
|
185
|
+
'fn',
|
|
186
|
+
-1,
|
|
187
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
188
|
+
(function(context, values) {
|
|
189
|
+
expect(values.length).toBe(5);
|
|
190
|
+
|
|
191
|
+
expect(sqlite3.value_type(values[0])).toEqual(SQLite.SQLITE_INTEGER);
|
|
192
|
+
expect(sqlite3.value_int(values[0])).toEqual(42);
|
|
193
|
+
expect(sqlite3.value_int64(values[0])).toEqual(42n);
|
|
194
|
+
expect(sqlite3.value_double(values[0])).toEqual(42.0);
|
|
195
|
+
expect(sqlite3.value(values[0])).toEqual(42);
|
|
196
|
+
|
|
197
|
+
expect(sqlite3.value_type(values[1])).toEqual(SQLite.SQLITE_FLOAT);
|
|
198
|
+
expect(sqlite3.value_double(values[1])).toEqual(3.14);
|
|
199
|
+
expect(sqlite3.value(values[1])).toEqual(3.14);
|
|
200
|
+
|
|
201
|
+
expect(sqlite3.value_type(values[2])).toEqual(SQLite.SQLITE_TEXT);
|
|
202
|
+
expect(sqlite3.value_text(values[2])).toEqual('hello');
|
|
203
|
+
expect(sqlite3.value(values[2])).toEqual('hello');
|
|
204
|
+
|
|
205
|
+
expect(sqlite3.value_type(values[3])).toEqual(SQLite.SQLITE_BLOB);
|
|
206
|
+
expect(sqlite3.value_blob(values[3])).toEqual(new Uint8Array([0x12, 0x34, 0x56]));
|
|
207
|
+
expect(sqlite3.value_bytes(values[3])).toEqual(3);
|
|
208
|
+
expect(sqlite3.value(values[3])).toEqual(new Uint8Array([0x12, 0x34, 0x56]));
|
|
209
|
+
|
|
210
|
+
expect(sqlite3.value_type(values[4])).toEqual(SQLite.SQLITE_NULL);
|
|
211
|
+
}));
|
|
212
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
213
|
+
|
|
214
|
+
rc = await sqlite3.exec(db, `
|
|
215
|
+
SELECT fn(42, 3.14, 'hello', x'123456', NULL)
|
|
216
|
+
`);
|
|
217
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should create an aggregate function', async function() {
|
|
221
|
+
let rc;
|
|
222
|
+
|
|
223
|
+
let product = 1;
|
|
224
|
+
rc = await sqlite3.create_function(
|
|
225
|
+
db,
|
|
226
|
+
'fn',
|
|
227
|
+
1,
|
|
228
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
229
|
+
null,
|
|
230
|
+
(function(context, values) {
|
|
231
|
+
const value = sqlite3.value_double(values[0]);
|
|
232
|
+
product *= value;
|
|
233
|
+
}),
|
|
234
|
+
(function(context) {
|
|
235
|
+
sqlite3.result_double(context, product);
|
|
236
|
+
}));
|
|
237
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
238
|
+
|
|
239
|
+
rc = await sqlite3.exec(db, `
|
|
240
|
+
SELECT fn(column1) FROM (VALUES (1), (2), (3), (4), (5));
|
|
241
|
+
`);
|
|
242
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
243
|
+
expect(product).toEqual(1 * 2 * 3 * 4 * 5);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should return asynchronously', async function() {
|
|
247
|
+
let rc;
|
|
248
|
+
|
|
249
|
+
rc = await sqlite3.create_function(
|
|
250
|
+
db,
|
|
251
|
+
'fn',
|
|
252
|
+
0,
|
|
253
|
+
SQLite.SQLITE_DETERMINISTIC, 0,
|
|
254
|
+
async (context, values) => {
|
|
255
|
+
await new Promise(resolve => setTimeout(resolve));
|
|
256
|
+
sqlite3.result_int(context, 42);
|
|
257
|
+
});
|
|
258
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
259
|
+
|
|
260
|
+
let result;
|
|
261
|
+
rc = await sqlite3.exec(db, 'SELECT fn()', row => result = row[0]);
|
|
262
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
263
|
+
expect(result).toEqual(42);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe(`${key} progress_handler`, function() {
|
|
268
|
+
let db;
|
|
269
|
+
beforeEach(async function() {
|
|
270
|
+
db = await sqlite3.open_v2(':memory:');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
afterEach(async function() {
|
|
274
|
+
await sqlite3.close(db);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should call progress handler', async function() {
|
|
278
|
+
let rc;
|
|
279
|
+
|
|
280
|
+
let count = 0;
|
|
281
|
+
await sqlite3.progress_handler(db, 1, () => ++count && 0, null);
|
|
282
|
+
|
|
283
|
+
rc = await sqlite3.exec(db, `
|
|
284
|
+
CREATE TABLE t AS
|
|
285
|
+
WITH RECURSIVE cnt(x) AS (
|
|
286
|
+
SELECT 1
|
|
287
|
+
UNION ALL
|
|
288
|
+
SELECT x+1 FROM cnt
|
|
289
|
+
LIMIT 100
|
|
290
|
+
)
|
|
291
|
+
SELECT x FROM cnt;
|
|
292
|
+
`);
|
|
293
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
294
|
+
expect(count).toBeGreaterThan(0);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should call asynchronous progress handler', async function() {
|
|
298
|
+
let rc;
|
|
299
|
+
|
|
300
|
+
let count = 0;
|
|
301
|
+
await sqlite3.progress_handler(db, 1, async () => ++count && 0, null);
|
|
302
|
+
|
|
303
|
+
rc = await sqlite3.exec(db, `
|
|
304
|
+
CREATE TABLE t AS
|
|
305
|
+
WITH RECURSIVE cnt(x) AS (
|
|
306
|
+
SELECT 1
|
|
307
|
+
UNION ALL
|
|
308
|
+
SELECT x+1 FROM cnt
|
|
309
|
+
LIMIT 100
|
|
310
|
+
)
|
|
311
|
+
SELECT x FROM cnt;
|
|
312
|
+
`);
|
|
313
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
314
|
+
expect(count).toBeGreaterThan(0);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe(`${key} set_authorizer`, function() {
|
|
319
|
+
let db;
|
|
320
|
+
beforeEach(async function() {
|
|
321
|
+
db = await sqlite3.open_v2(':memory:');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
afterEach(async function() {
|
|
325
|
+
await sqlite3.close(db);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should call authorizer', async function() {
|
|
329
|
+
let rc;
|
|
330
|
+
|
|
331
|
+
const authorizations = [];
|
|
332
|
+
rc = sqlite3.set_authorizer(db, (_, iActionCode, p3, p4, p5, p6) => {
|
|
333
|
+
authorizations.push([iActionCode, p3, p4, p5, p6]);
|
|
334
|
+
return SQLite.SQLITE_OK;
|
|
335
|
+
});
|
|
336
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
337
|
+
|
|
338
|
+
rc = await sqlite3.exec(db, 'CREATE TABLE t(x)');
|
|
339
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
340
|
+
|
|
341
|
+
expect(authorizations.length).toBeGreaterThan(0);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should deny authorization', async function() {
|
|
345
|
+
let rc;
|
|
346
|
+
|
|
347
|
+
rc = sqlite3.set_authorizer(db, (_, iActionCode, p3, p4, p5, p6) => {
|
|
348
|
+
return SQLite.SQLITE_DENY;
|
|
349
|
+
});
|
|
350
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
351
|
+
|
|
352
|
+
const result = sqlite3.exec(db, 'CREATE TABLE t(x)');
|
|
353
|
+
await expectAsync(result).toBeRejectedWith(new Error('not authorized'));
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should call async authorizer', async function() {
|
|
357
|
+
let rc;
|
|
358
|
+
|
|
359
|
+
const authorizations = [];
|
|
360
|
+
rc = sqlite3.set_authorizer(db, async (_, iActionCode, p3, p4, p5, p6) => {
|
|
361
|
+
authorizations.push([iActionCode, p3, p4, p5, p6]);
|
|
362
|
+
return SQLite.SQLITE_OK;
|
|
363
|
+
});
|
|
364
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
365
|
+
|
|
366
|
+
rc = await sqlite3.exec(db, 'CREATE TABLE t(x)');
|
|
367
|
+
expect(rc).toEqual(SQLite.SQLITE_OK);
|
|
368
|
+
|
|
369
|
+
expect(authorizations.length).toBeGreaterThan(0);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
package/test/sql.test.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TestContext } from "./TestContext.js";
|
|
2
|
+
import { sql_0001 } from "./sql_0001.js";
|
|
3
|
+
import { sql_0002 } from "./sql_0002.js";
|
|
4
|
+
import { sql_0003 } from "./sql_0003.js";
|
|
5
|
+
import { sql_0004 } from "./sql_0004.js";
|
|
6
|
+
import { sql_0005 } from "./sql_0005.js";
|
|
7
|
+
|
|
8
|
+
const ALL_BUILDS = ['default', 'asyncify', 'jspi'];
|
|
9
|
+
const ASYNC_BUILDS = ['asyncify', 'jspi'];
|
|
10
|
+
|
|
11
|
+
// Not all browsers support JSPI yet.
|
|
12
|
+
const supportsJSPI = await TestContext.supportsJSPI();
|
|
13
|
+
|
|
14
|
+
/** @type {Map<string, string[]>} */
|
|
15
|
+
const CONFIGS = new Map([
|
|
16
|
+
['', ALL_BUILDS],
|
|
17
|
+
['MemoryVFS', ALL_BUILDS],
|
|
18
|
+
['AccessHandlePoolVFS', ALL_BUILDS],
|
|
19
|
+
['OPFSCoopSyncVFS', ALL_BUILDS],
|
|
20
|
+
['MemoryAsyncVFS', ASYNC_BUILDS],
|
|
21
|
+
['IDBBatchAtomicVFS', ASYNC_BUILDS],
|
|
22
|
+
['IDBMirrorVFS', ASYNC_BUILDS],
|
|
23
|
+
['OPFSAdaptiveVFS', ASYNC_BUILDS],
|
|
24
|
+
['OPFSAnyContextVFS', ASYNC_BUILDS],
|
|
25
|
+
['OPFSPermutedVFS', ASYNC_BUILDS],
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const DISALLOWS_PAGE_SIZE_CHANGE = ['IDBBatchAtomicVFS', 'IDBMirrorVFS', 'OPFSPermutedVFS', 'FLOOR'];
|
|
29
|
+
const NOT_PERSISTENT = ['', 'MemoryVFS', 'MemoryAsyncVFS'];
|
|
30
|
+
const SINGLE_CONNECTION = ['', 'MemoryVFS', 'MemoryAsyncVFS', 'AccessHandlePoolVFS'];
|
|
31
|
+
|
|
32
|
+
describe('SQL', function() {
|
|
33
|
+
for (const [config, builds] of CONFIGS) {
|
|
34
|
+
describe(config, function() {
|
|
35
|
+
for (const build of builds) {
|
|
36
|
+
// Skip JSPI tests if the browser does not support it.
|
|
37
|
+
if (build === 'jspi' && !supportsJSPI) continue;
|
|
38
|
+
|
|
39
|
+
describe(build, function() {
|
|
40
|
+
sqlSpecs(build, config);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
function sqlSpecs(build, config) {
|
|
48
|
+
const context = new TestContext({ build, config });
|
|
49
|
+
|
|
50
|
+
sql_0001(context);
|
|
51
|
+
sql_0002(context);
|
|
52
|
+
if (!DISALLOWS_PAGE_SIZE_CHANGE.includes(config)) {
|
|
53
|
+
// These tests change the page size.
|
|
54
|
+
sql_0003(context);
|
|
55
|
+
}
|
|
56
|
+
if (!NOT_PERSISTENT.includes(config)) {
|
|
57
|
+
// These tests require persistent storage.
|
|
58
|
+
sql_0004(context);
|
|
59
|
+
}
|
|
60
|
+
if (!SINGLE_CONNECTION.includes(config)) {
|
|
61
|
+
// These tests require multiple connections.
|
|
62
|
+
sql_0005(context);
|
|
63
|
+
}
|
|
64
|
+
}
|
package/test/sql_0001.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
|
|
3
|
+
export function sql_0001(context) {
|
|
4
|
+
describe('sql_0001', function() {
|
|
5
|
+
let proxy, sqlite3, db;
|
|
6
|
+
beforeEach(async function() {
|
|
7
|
+
proxy = await context.create();
|
|
8
|
+
sqlite3 = proxy.sqlite3;
|
|
9
|
+
db = await sqlite3.open_v2('demo');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async function() {
|
|
13
|
+
await sqlite3.close(db);
|
|
14
|
+
await context.destroy(proxy);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should rollback a transaction', async function() {
|
|
18
|
+
let count;
|
|
19
|
+
await sqlite3.exec(db, `
|
|
20
|
+
CREATE TABLE foo (x PRIMARY KEY);
|
|
21
|
+
INSERT INTO foo VALUES ('foo'), ('bar'), ('baz');
|
|
22
|
+
SELECT COUNT(*) FROM foo;
|
|
23
|
+
`, Comlink.proxy(row => count = row[0]));
|
|
24
|
+
expect(count).toBe(3);
|
|
25
|
+
|
|
26
|
+
count = undefined;
|
|
27
|
+
await sqlite3.exec(db, `
|
|
28
|
+
BEGIN TRANSACTION;
|
|
29
|
+
WITH numbers(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 100)
|
|
30
|
+
INSERT INTO foo SELECT * FROM numbers;
|
|
31
|
+
SELECT COUNT(*) FROM foo;
|
|
32
|
+
`, Comlink.proxy(row => count = row[0]));
|
|
33
|
+
expect(count).toBe(103);
|
|
34
|
+
|
|
35
|
+
count = undefined;
|
|
36
|
+
await sqlite3.exec(db, `
|
|
37
|
+
ROLLBACK;
|
|
38
|
+
SELECT COUNT(*) FROM foo;
|
|
39
|
+
`, Comlink.proxy(row => count = row[0]));
|
|
40
|
+
expect(count).toBe(3);
|
|
41
|
+
|
|
42
|
+
let checkStatus;
|
|
43
|
+
await sqlite3.exec(db, `
|
|
44
|
+
PRAGMA integrity_check;
|
|
45
|
+
`, Comlink.proxy(row => checkStatus = row[0]));
|
|
46
|
+
expect(checkStatus).toBe('ok');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
package/test/sql_0002.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
|
|
3
|
+
export function sql_0002(context) {
|
|
4
|
+
describe('sql_0002', function() {
|
|
5
|
+
let proxy, sqlite3, db;
|
|
6
|
+
beforeEach(async function() {
|
|
7
|
+
proxy = await context.create();
|
|
8
|
+
sqlite3 = proxy.sqlite3;
|
|
9
|
+
db = await sqlite3.open_v2('demo');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async function() {
|
|
13
|
+
await sqlite3.close(db);
|
|
14
|
+
await context.destroy(proxy);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should vacuum to minimize page count', async function() {
|
|
18
|
+
await sqlite3.exec(db, `
|
|
19
|
+
CREATE TABLE t AS
|
|
20
|
+
WITH numbers(n) AS
|
|
21
|
+
(SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
|
|
22
|
+
SELECT n FROM numbers;
|
|
23
|
+
`);
|
|
24
|
+
|
|
25
|
+
let nPagesBeforeVacuum;
|
|
26
|
+
await sqlite3.exec(db, `
|
|
27
|
+
PRAGMA page_count;
|
|
28
|
+
`, Comlink.proxy(row => nPagesBeforeVacuum = row[0]));
|
|
29
|
+
|
|
30
|
+
await sqlite3.exec(db, `
|
|
31
|
+
DELETE FROM t WHERE sqrt(n) != floor(sqrt(n));
|
|
32
|
+
`);
|
|
33
|
+
|
|
34
|
+
await sqlite3.exec(db, `
|
|
35
|
+
VACUUM;
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
let nPagesAfterVacuum;
|
|
39
|
+
await sqlite3.exec(db, `
|
|
40
|
+
PRAGMA page_count;
|
|
41
|
+
`, Comlink.proxy(row => nPagesAfterVacuum = row[0]));
|
|
42
|
+
|
|
43
|
+
expect(nPagesAfterVacuum).toBeLessThan(nPagesBeforeVacuum);
|
|
44
|
+
|
|
45
|
+
let checkStatus;
|
|
46
|
+
await sqlite3.exec(db, `
|
|
47
|
+
PRAGMA integrity_check;
|
|
48
|
+
`, Comlink.proxy(row => checkStatus = row[0]));
|
|
49
|
+
expect(checkStatus).toBe('ok');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
package/test/sql_0003.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
|
|
3
|
+
export function sql_0003(context) {
|
|
4
|
+
describe('sql_0003', function() {
|
|
5
|
+
let proxy, sqlite3, db;
|
|
6
|
+
beforeEach(async function() {
|
|
7
|
+
proxy = await context.create();
|
|
8
|
+
sqlite3 = proxy.sqlite3;
|
|
9
|
+
db = await sqlite3.open_v2('demo');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async function() {
|
|
13
|
+
await sqlite3.close(db);
|
|
14
|
+
await context.destroy(proxy);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should vacuum to decrease page size', async function() {
|
|
18
|
+
await sqlite3.exec(db, `
|
|
19
|
+
PRAGMA page_size=8192;
|
|
20
|
+
CREATE TABLE t AS
|
|
21
|
+
WITH numbers(n) AS
|
|
22
|
+
(SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
|
|
23
|
+
SELECT n FROM numbers;
|
|
24
|
+
`);
|
|
25
|
+
|
|
26
|
+
let pageSizeBeforeVacuum;
|
|
27
|
+
await sqlite3.exec(db, `
|
|
28
|
+
PRAGMA page_size;
|
|
29
|
+
`, Comlink.proxy(row => pageSizeBeforeVacuum = row[0]));
|
|
30
|
+
expect(pageSizeBeforeVacuum).toBe(8192);
|
|
31
|
+
|
|
32
|
+
await sqlite3.exec(db, `
|
|
33
|
+
PRAGMA page_size=4096;
|
|
34
|
+
VACUUM;
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
let pageSizeAfterVacuum;
|
|
38
|
+
await sqlite3.exec(db, `
|
|
39
|
+
PRAGMA page_size;
|
|
40
|
+
`, Comlink.proxy(row => pageSizeAfterVacuum = row[0]));
|
|
41
|
+
expect(pageSizeAfterVacuum).toBe(4096);
|
|
42
|
+
|
|
43
|
+
let checkStatus;
|
|
44
|
+
await sqlite3.exec(db, `
|
|
45
|
+
PRAGMA integrity_check;
|
|
46
|
+
`, Comlink.proxy(row => checkStatus = row[0]));
|
|
47
|
+
expect(checkStatus).toBe('ok');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should vacuum to increase page size', async function() {
|
|
51
|
+
await sqlite3.exec(db, `
|
|
52
|
+
PRAGMA page_size=8192;
|
|
53
|
+
CREATE TABLE t AS
|
|
54
|
+
WITH numbers(n) AS
|
|
55
|
+
(SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
|
|
56
|
+
SELECT n FROM numbers;
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
let pageSizeBeforeVacuum;
|
|
60
|
+
await sqlite3.exec(db, `
|
|
61
|
+
PRAGMA page_size;
|
|
62
|
+
`, Comlink.proxy(row => pageSizeBeforeVacuum = row[0]));
|
|
63
|
+
expect(pageSizeBeforeVacuum).toBe(8192);
|
|
64
|
+
|
|
65
|
+
await sqlite3.exec(db, `
|
|
66
|
+
PRAGMA page_size=16384;
|
|
67
|
+
VACUUM;
|
|
68
|
+
`);
|
|
69
|
+
|
|
70
|
+
let pageSizeAfterVacuum;
|
|
71
|
+
await sqlite3.exec(db, `
|
|
72
|
+
PRAGMA page_size;
|
|
73
|
+
`, Comlink.proxy(row => pageSizeAfterVacuum = row[0]));
|
|
74
|
+
expect(pageSizeAfterVacuum).toBe(16384);
|
|
75
|
+
|
|
76
|
+
let checkStatus;
|
|
77
|
+
await sqlite3.exec(db, `
|
|
78
|
+
PRAGMA integrity_check;
|
|
79
|
+
`, Comlink.proxy(row => checkStatus = row[0]));
|
|
80
|
+
expect(checkStatus).toBe('ok');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
package/test/sql_0004.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as Comlink from 'comlink';
|
|
2
|
+
|
|
3
|
+
export function sql_0004(context) {
|
|
4
|
+
const cleanup = [];
|
|
5
|
+
beforeEach(async function() {
|
|
6
|
+
cleanup.splice(0);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(async function() {
|
|
10
|
+
for (const fn of cleanup) {
|
|
11
|
+
await fn();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('sql_0004', function() {
|
|
16
|
+
it('should recover after crash', async function() {
|
|
17
|
+
const proxyA = await context.create();
|
|
18
|
+
try {
|
|
19
|
+
const sqlite3 = proxyA.sqlite3;
|
|
20
|
+
const db = await sqlite3.open_v2('demo');
|
|
21
|
+
await sqlite3.exec(db, `
|
|
22
|
+
PRAGMA cache_size=0;
|
|
23
|
+
CREATE TABLE t(x);
|
|
24
|
+
INSERT INTO t VALUES (1), (2), (3);
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
let sum;
|
|
28
|
+
await sqlite3.exec(db, `
|
|
29
|
+
SELECT sum(x) FROM t;
|
|
30
|
+
`, Comlink.proxy(row => sum = row[0]));
|
|
31
|
+
expect(sum).toBe(6);
|
|
32
|
+
|
|
33
|
+
let check;
|
|
34
|
+
await sqlite3.exec(db, `
|
|
35
|
+
PRAGMA integrity_check;
|
|
36
|
+
`, Comlink.proxy(row => check = row[0]));
|
|
37
|
+
expect(check).toBe('ok');
|
|
38
|
+
|
|
39
|
+
// Begin a transaction but don't commit it.
|
|
40
|
+
await sqlite3.exec(db, `
|
|
41
|
+
BEGIN TRANSACTION;
|
|
42
|
+
WITH RECURSIVE cnt(x) AS
|
|
43
|
+
(SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 10000)
|
|
44
|
+
INSERT INTO t SELECT * FROM cnt;
|
|
45
|
+
`);
|
|
46
|
+
} finally {
|
|
47
|
+
await context.destroy(proxyA);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
51
|
+
|
|
52
|
+
const proxyB = await context.create({ reset: false });
|
|
53
|
+
try {
|
|
54
|
+
const sqlite3 = proxyB.sqlite3;
|
|
55
|
+
const db = await sqlite3.open_v2('demo');
|
|
56
|
+
|
|
57
|
+
let sum;
|
|
58
|
+
await sqlite3.exec(db, `
|
|
59
|
+
SELECT sum(x) FROM t;
|
|
60
|
+
`, Comlink.proxy(row => sum = row[0]));
|
|
61
|
+
expect(sum).toBe(6);
|
|
62
|
+
|
|
63
|
+
let check;
|
|
64
|
+
await sqlite3.exec(db, `
|
|
65
|
+
PRAGMA integrity_check;
|
|
66
|
+
`, Comlink.proxy(row => check = row[0]));
|
|
67
|
+
expect(check).toBe('ok');
|
|
68
|
+
|
|
69
|
+
await sqlite3.exec(db, `
|
|
70
|
+
INSERT INTO t VALUES (4), (5);
|
|
71
|
+
`);
|
|
72
|
+
await sqlite3.exec(db, `
|
|
73
|
+
SELECT sum(x) FROM t;
|
|
74
|
+
`, Comlink.proxy(row => sum = row[0]));
|
|
75
|
+
expect(sum).toBe(15);
|
|
76
|
+
} finally {
|
|
77
|
+
await context.destroy(proxyB);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|