flatsql 0.2.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 +190 -0
- package/README.md +489 -0
- package/dist/btree/btree.d.ts +34 -0
- package/dist/btree/btree.d.ts.map +1 -0
- package/dist/btree/btree.js +235 -0
- package/dist/btree/btree.js.map +1 -0
- package/dist/btree/index.d.ts +3 -0
- package/dist/btree/index.d.ts.map +1 -0
- package/dist/btree/index.js +3 -0
- package/dist/btree/index.js.map +1 -0
- package/dist/btree/types.d.ts +36 -0
- package/dist/btree/types.d.ts.map +1 -0
- package/dist/btree/types.js +55 -0
- package/dist/btree/types.js.map +1 -0
- package/dist/core/database.d.ts +45 -0
- package/dist/core/database.d.ts.map +1 -0
- package/dist/core/database.js +248 -0
- package/dist/core/database.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/table-store.d.ts +28 -0
- package/dist/core/table-store.d.ts.map +1 -0
- package/dist/core/table-store.js +153 -0
- package/dist/core/table-store.js.map +1 -0
- package/dist/flatbuffer-accessor.d.ts +35 -0
- package/dist/flatbuffer-accessor.d.ts.map +1 -0
- package/dist/flatbuffer-accessor.js +114 -0
- package/dist/flatbuffer-accessor.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/parser.d.ts +25 -0
- package/dist/schema/parser.d.ts.map +1 -0
- package/dist/schema/parser.js +394 -0
- package/dist/schema/parser.js.map +1 -0
- package/dist/schema/types.d.ts +33 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +29 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/stacked-flatbuffers.d.ts +38 -0
- package/dist/storage/stacked-flatbuffers.d.ts.map +1 -0
- package/dist/storage/stacked-flatbuffers.js +253 -0
- package/dist/storage/stacked-flatbuffers.js.map +1 -0
- package/dist/storage/types.d.ts +23 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +27 -0
- package/dist/storage/types.js.map +1 -0
- package/package.json +79 -0
- package/wasm/flatsql-client.js +280 -0
- package/wasm/flatsql.js +2 -0
- package/wasm/flatsql.wasm +0 -0
- package/wasm/flatsql.worker.js +209 -0
- package/wasm/index.d.ts +234 -0
- package/wasm/index.js +463 -0
package/wasm/index.js
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
// FlatSQL JavaScript API - Uses C exports (worker-compatible)
|
|
2
|
+
// This wrapper uses cwrap/ccall instead of embind to avoid worker issues
|
|
3
|
+
|
|
4
|
+
import FlatSQLModule from './flatsql.js';
|
|
5
|
+
|
|
6
|
+
let Module = null;
|
|
7
|
+
let api = null;
|
|
8
|
+
|
|
9
|
+
// Security: Track if integrity was verified
|
|
10
|
+
let integrityVerified = false;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Compute SHA-384 hash of data and return as base64
|
|
14
|
+
* Works in both Node.js and browser environments
|
|
15
|
+
* @param {ArrayBuffer} data
|
|
16
|
+
* @returns {Promise<string>}
|
|
17
|
+
*/
|
|
18
|
+
async function computeSHA384(data) {
|
|
19
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
20
|
+
// Browser or Node.js 18+
|
|
21
|
+
const hashBuffer = await crypto.subtle.digest('SHA-384', data);
|
|
22
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
23
|
+
// Convert to base64
|
|
24
|
+
if (typeof btoa !== 'undefined') {
|
|
25
|
+
return btoa(String.fromCharCode(...hashArray));
|
|
26
|
+
} else {
|
|
27
|
+
return Buffer.from(hashArray).toString('base64');
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
// Node.js fallback using dynamic import
|
|
31
|
+
const cryptoModule = await import('crypto');
|
|
32
|
+
const hash = cryptoModule.createHash('sha384');
|
|
33
|
+
hash.update(Buffer.from(data));
|
|
34
|
+
return hash.digest('base64');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Verify WASM binary integrity
|
|
40
|
+
* @param {ArrayBuffer} wasmBinary - The WASM binary data
|
|
41
|
+
* @param {string} expectedHash - Expected SHA-384 hash (base64)
|
|
42
|
+
* @returns {Promise<boolean>}
|
|
43
|
+
*/
|
|
44
|
+
async function verifyWASMIntegrity(wasmBinary, expectedHash) {
|
|
45
|
+
const computedHash = await computeSHA384(wasmBinary);
|
|
46
|
+
return computedHash === expectedHash;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load integrity.json if available
|
|
51
|
+
* @param {string} [basePath] - Base path for integrity.json
|
|
52
|
+
* @returns {Promise<{hash: string, sri: string, size: number} | null>}
|
|
53
|
+
*/
|
|
54
|
+
async function loadIntegrityFile(basePath = '') {
|
|
55
|
+
try {
|
|
56
|
+
if (typeof fetch !== 'undefined') {
|
|
57
|
+
// Browser or Node.js with fetch
|
|
58
|
+
const url = basePath ? `${basePath}/integrity.json` : new URL('./integrity.json', import.meta.url).href;
|
|
59
|
+
const response = await fetch(url);
|
|
60
|
+
if (response.ok) {
|
|
61
|
+
return await response.json();
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Node.js fallback
|
|
65
|
+
const fs = await import('fs');
|
|
66
|
+
const path = await import('path');
|
|
67
|
+
const url = await import('url');
|
|
68
|
+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
69
|
+
const integrityPath = path.join(__dirname, 'integrity.json');
|
|
70
|
+
if (fs.existsSync(integrityPath)) {
|
|
71
|
+
return JSON.parse(fs.readFileSync(integrityPath, 'utf8'));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Integrity file not available - this is OK in development
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {Object} InitOptions
|
|
82
|
+
* @property {string} [integrity] - Expected SHA-384 hash for WASM verification (base64)
|
|
83
|
+
* @property {string} [wasmPath] - Custom path to WASM files directory
|
|
84
|
+
* @property {boolean} [skipIntegrityCheck] - Skip integrity verification (not recommended for production)
|
|
85
|
+
* @property {boolean} [requireIntegrity] - Fail if integrity cannot be verified (default: false)
|
|
86
|
+
* @property {function} [moduleFactory] - Custom Emscripten module factory
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the FlatSQL WASM module with optional integrity verification
|
|
91
|
+
* @param {function|InitOptions} [moduleFactoryOrOptions] - Module factory or initialization options
|
|
92
|
+
* @returns {Promise<FlatSQL>}
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // Basic usage (auto-loads integrity.json if available)
|
|
96
|
+
* const flatsql = await initFlatSQL();
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // With explicit integrity hash
|
|
100
|
+
* const flatsql = await initFlatSQL({
|
|
101
|
+
* integrity: 'base64-hash-here',
|
|
102
|
+
* requireIntegrity: true
|
|
103
|
+
* });
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Skip integrity check (development only)
|
|
107
|
+
* const flatsql = await initFlatSQL({ skipIntegrityCheck: true });
|
|
108
|
+
*/
|
|
109
|
+
export async function initFlatSQL(moduleFactoryOrOptions) {
|
|
110
|
+
let moduleFactory = FlatSQLModule;
|
|
111
|
+
let options = {};
|
|
112
|
+
|
|
113
|
+
// Parse arguments - support both legacy and new API
|
|
114
|
+
if (moduleFactoryOrOptions) {
|
|
115
|
+
if (typeof moduleFactoryOrOptions === 'function') {
|
|
116
|
+
// Legacy: moduleFactory passed directly
|
|
117
|
+
moduleFactory = moduleFactoryOrOptions;
|
|
118
|
+
} else if (typeof moduleFactoryOrOptions === 'object') {
|
|
119
|
+
// New: options object
|
|
120
|
+
options = moduleFactoryOrOptions;
|
|
121
|
+
if (options.moduleFactory) {
|
|
122
|
+
moduleFactory = options.moduleFactory;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Determine expected integrity hash
|
|
128
|
+
let expectedIntegrity = options.integrity || null;
|
|
129
|
+
|
|
130
|
+
// Load integrity file if no explicit integrity provided
|
|
131
|
+
if (!expectedIntegrity && !options.skipIntegrityCheck) {
|
|
132
|
+
const integrityData = await loadIntegrityFile(options.wasmPath);
|
|
133
|
+
if (integrityData) {
|
|
134
|
+
expectedIntegrity = integrityData.hash;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check if integrity is required but not available
|
|
139
|
+
if (options.requireIntegrity && !expectedIntegrity) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
'WASM integrity verification required but no integrity hash available. ' +
|
|
142
|
+
'Ensure integrity.json exists or pass integrity option.'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Create module configuration
|
|
147
|
+
const moduleConfig = {};
|
|
148
|
+
|
|
149
|
+
// If we have an expected hash, use custom WASM instantiation with verification
|
|
150
|
+
if (expectedIntegrity && !options.skipIntegrityCheck) {
|
|
151
|
+
moduleConfig.instantiateWasm = async (imports, successCallback) => {
|
|
152
|
+
try {
|
|
153
|
+
// Determine WASM path
|
|
154
|
+
let wasmUrl;
|
|
155
|
+
if (options.wasmPath) {
|
|
156
|
+
wasmUrl = `${options.wasmPath}/flatsql.wasm`;
|
|
157
|
+
} else {
|
|
158
|
+
wasmUrl = new URL('./flatsql.wasm', import.meta.url).href;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Fetch WASM binary
|
|
162
|
+
let wasmBinary;
|
|
163
|
+
if (typeof fetch !== 'undefined') {
|
|
164
|
+
const response = await fetch(wasmUrl);
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
167
|
+
}
|
|
168
|
+
wasmBinary = await response.arrayBuffer();
|
|
169
|
+
} else {
|
|
170
|
+
// Node.js fallback
|
|
171
|
+
const fs = await import('fs');
|
|
172
|
+
const path = await import('path');
|
|
173
|
+
const url = await import('url');
|
|
174
|
+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
175
|
+
const wasmPath = path.join(__dirname, 'flatsql.wasm');
|
|
176
|
+
wasmBinary = fs.readFileSync(wasmPath).buffer;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Verify integrity
|
|
180
|
+
const isValid = await verifyWASMIntegrity(wasmBinary, expectedIntegrity);
|
|
181
|
+
if (!isValid) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
'WASM integrity check failed: hash mismatch. ' +
|
|
184
|
+
'The WASM binary may have been tampered with or corrupted.'
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Mark as verified
|
|
189
|
+
integrityVerified = true;
|
|
190
|
+
|
|
191
|
+
// Instantiate verified WASM
|
|
192
|
+
const result = await WebAssembly.instantiate(wasmBinary, imports);
|
|
193
|
+
successCallback(result.instance);
|
|
194
|
+
return result.instance.exports;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error(`WASM integrity verification failed: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Initialize module
|
|
202
|
+
Module = await moduleFactory(moduleConfig);
|
|
203
|
+
|
|
204
|
+
// Wrap C functions using cwrap
|
|
205
|
+
api = {
|
|
206
|
+
// Database lifecycle
|
|
207
|
+
createDb: Module.cwrap('flatsql_create_db', 'number', ['string', 'string']),
|
|
208
|
+
destroyDb: Module.cwrap('flatsql_destroy_db', null, ['number']),
|
|
209
|
+
registerFileId: Module.cwrap('flatsql_register_file_id', null, ['number', 'string', 'string']),
|
|
210
|
+
enableDemoExtractors: Module.cwrap('flatsql_enable_demo_extractors', null, ['number']),
|
|
211
|
+
|
|
212
|
+
// Data ingestion
|
|
213
|
+
ingest: Module.cwrap('flatsql_ingest', 'number', ['number', 'number', 'number']),
|
|
214
|
+
ingestOne: Module.cwrap('flatsql_ingest_one', 'number', ['number', 'number', 'number']),
|
|
215
|
+
|
|
216
|
+
// Source-aware ingestion
|
|
217
|
+
registerSource: Module.cwrap('flatsql_register_source', null, ['number', 'string']),
|
|
218
|
+
createUnifiedViews: Module.cwrap('flatsql_create_unified_views', null, ['number']),
|
|
219
|
+
ingestWithSource: Module.cwrap('flatsql_ingest_with_source', 'number', ['number', 'number', 'number', 'string']),
|
|
220
|
+
ingestOneWithSource: Module.cwrap('flatsql_ingest_one_with_source', 'number', ['number', 'number', 'number', 'string']),
|
|
221
|
+
getSourcesCount: Module.cwrap('flatsql_get_sources_count', 'number', ['number']),
|
|
222
|
+
getSourceName: Module.cwrap('flatsql_get_source_name', 'string', ['number']),
|
|
223
|
+
|
|
224
|
+
// Query execution
|
|
225
|
+
query: Module.cwrap('flatsql_query', 'number', ['number', 'string']),
|
|
226
|
+
getError: Module.cwrap('flatsql_get_error', 'string', []),
|
|
227
|
+
|
|
228
|
+
// Result access
|
|
229
|
+
resultColumnCount: Module.cwrap('flatsql_result_column_count', 'number', []),
|
|
230
|
+
resultRowCount: Module.cwrap('flatsql_result_row_count', 'number', []),
|
|
231
|
+
resultColumnName: Module.cwrap('flatsql_result_column_name', 'string', ['number']),
|
|
232
|
+
resultCellType: Module.cwrap('flatsql_result_cell_type', 'number', ['number', 'number']),
|
|
233
|
+
resultCellNumber: Module.cwrap('flatsql_result_cell_number', 'number', ['number', 'number']),
|
|
234
|
+
resultCellString: Module.cwrap('flatsql_result_cell_string', 'string', ['number', 'number']),
|
|
235
|
+
resultCellBlob: Module.cwrap('flatsql_result_cell_blob', 'number', ['number', 'number']),
|
|
236
|
+
resultCellBlobSize: Module.cwrap('flatsql_result_cell_blob_size', 'number', ['number', 'number']),
|
|
237
|
+
|
|
238
|
+
// Export/Import
|
|
239
|
+
exportData: Module.cwrap('flatsql_export_data', 'number', ['number']),
|
|
240
|
+
exportSize: Module.cwrap('flatsql_export_size', 'number', []),
|
|
241
|
+
loadAndRebuild: Module.cwrap('flatsql_load_and_rebuild', null, ['number', 'number', 'number']),
|
|
242
|
+
|
|
243
|
+
// Test helpers
|
|
244
|
+
createTestUser: Module.cwrap('flatsql_create_test_user', 'number', ['number', 'string', 'string', 'number']),
|
|
245
|
+
createTestPost: Module.cwrap('flatsql_create_test_post', 'number', ['number', 'number', 'string']),
|
|
246
|
+
testBufferSize: Module.cwrap('flatsql_test_buffer_size', 'number', []),
|
|
247
|
+
|
|
248
|
+
// Stats
|
|
249
|
+
getStatsCount: Module.cwrap('flatsql_get_stats_count', 'number', ['number']),
|
|
250
|
+
getStatTableName: Module.cwrap('flatsql_get_stat_table_name', 'string', ['number']),
|
|
251
|
+
getStatFileId: Module.cwrap('flatsql_get_stat_file_id', 'string', ['number']),
|
|
252
|
+
getStatRecordCount: Module.cwrap('flatsql_get_stat_record_count', 'number', ['number']),
|
|
253
|
+
|
|
254
|
+
// Delete support
|
|
255
|
+
markDeleted: Module.cwrap('flatsql_mark_deleted', null, ['number', 'string', 'number']),
|
|
256
|
+
getDeletedCount: Module.cwrap('flatsql_get_deleted_count', 'number', ['number', 'string']),
|
|
257
|
+
clearTombstones: Module.cwrap('flatsql_clear_tombstones', null, ['number', 'string']),
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return new FlatSQL();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Check if WASM was loaded with integrity verification
|
|
265
|
+
* @returns {boolean}
|
|
266
|
+
*/
|
|
267
|
+
export function wasIntegrityVerified() {
|
|
268
|
+
return integrityVerified;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// High-level FlatSQL API class
|
|
272
|
+
export class FlatSQL {
|
|
273
|
+
createDatabase(schema, dbName = 'default') {
|
|
274
|
+
const handle = api.createDb(schema, dbName);
|
|
275
|
+
return new FlatSQLDatabase(handle);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Create test FlatBuffers
|
|
279
|
+
createTestUser(id, name, email, age) {
|
|
280
|
+
const ptr = api.createTestUser(id, name, email, age);
|
|
281
|
+
const size = api.testBufferSize();
|
|
282
|
+
return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
createTestPost(id, userId, title) {
|
|
286
|
+
const ptr = api.createTestPost(id, userId, title);
|
|
287
|
+
const size = api.testBufferSize();
|
|
288
|
+
return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Check if WASM was loaded with integrity verification
|
|
293
|
+
* @returns {boolean}
|
|
294
|
+
*/
|
|
295
|
+
wasIntegrityVerified() {
|
|
296
|
+
return integrityVerified;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Database wrapper class
|
|
301
|
+
export class FlatSQLDatabase {
|
|
302
|
+
constructor(handle) {
|
|
303
|
+
this._handle = handle;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
destroy() {
|
|
307
|
+
if (this._handle) {
|
|
308
|
+
api.destroyDb(this._handle);
|
|
309
|
+
this._handle = null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
registerFileId(fileId, tableName) {
|
|
314
|
+
api.registerFileId(this._handle, fileId, tableName);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
enableDemoExtractors() {
|
|
318
|
+
api.enableDemoExtractors(this._handle);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Ingest data from Uint8Array (routes to base tables or source tables)
|
|
322
|
+
ingest(data, source = null) {
|
|
323
|
+
const ptr = Module._malloc(data.length);
|
|
324
|
+
Module.HEAPU8.set(data, ptr);
|
|
325
|
+
let count;
|
|
326
|
+
if (source) {
|
|
327
|
+
count = api.ingestWithSource(this._handle, ptr, data.length, source);
|
|
328
|
+
} else {
|
|
329
|
+
count = api.ingest(this._handle, ptr, data.length);
|
|
330
|
+
}
|
|
331
|
+
Module._free(ptr);
|
|
332
|
+
return count;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
ingestOne(data, source = null) {
|
|
336
|
+
const ptr = Module._malloc(data.length);
|
|
337
|
+
Module.HEAPU8.set(data, ptr);
|
|
338
|
+
let count;
|
|
339
|
+
if (source) {
|
|
340
|
+
count = api.ingestOneWithSource(this._handle, ptr, data.length, source);
|
|
341
|
+
} else {
|
|
342
|
+
count = api.ingestOne(this._handle, ptr, data.length);
|
|
343
|
+
}
|
|
344
|
+
Module._free(ptr);
|
|
345
|
+
return count;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Register a named data source for source-aware ingestion
|
|
349
|
+
// Creates source-specific tables: User@siteA, Post@siteA, etc.
|
|
350
|
+
registerSource(sourceName) {
|
|
351
|
+
api.registerSource(this._handle, sourceName);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Create unified views for cross-source queries
|
|
355
|
+
// Must be called after registering all sources and file IDs
|
|
356
|
+
createUnifiedViews() {
|
|
357
|
+
api.createUnifiedViews(this._handle);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// List registered sources
|
|
361
|
+
listSources() {
|
|
362
|
+
const count = api.getSourcesCount(this._handle);
|
|
363
|
+
const sources = [];
|
|
364
|
+
for (let i = 0; i < count; i++) {
|
|
365
|
+
sources.push(api.getSourceName(i));
|
|
366
|
+
}
|
|
367
|
+
return sources;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
query(sql) {
|
|
371
|
+
const success = api.query(this._handle, sql);
|
|
372
|
+
if (!success) {
|
|
373
|
+
throw new Error(api.getError());
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Read results
|
|
377
|
+
const colCount = api.resultColumnCount();
|
|
378
|
+
const rowCount = api.resultRowCount();
|
|
379
|
+
|
|
380
|
+
const columns = [];
|
|
381
|
+
for (let i = 0; i < colCount; i++) {
|
|
382
|
+
columns.push(api.resultColumnName(i));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const rows = [];
|
|
386
|
+
for (let r = 0; r < rowCount; r++) {
|
|
387
|
+
const row = [];
|
|
388
|
+
for (let c = 0; c < colCount; c++) {
|
|
389
|
+
const type = api.resultCellType(r, c);
|
|
390
|
+
switch (type) {
|
|
391
|
+
case 0: // null
|
|
392
|
+
row.push(null);
|
|
393
|
+
break;
|
|
394
|
+
case 1: // bool
|
|
395
|
+
row.push(api.resultCellNumber(r, c) !== 0);
|
|
396
|
+
break;
|
|
397
|
+
case 2: // int32
|
|
398
|
+
case 3: // int64
|
|
399
|
+
case 4: // double
|
|
400
|
+
row.push(api.resultCellNumber(r, c));
|
|
401
|
+
break;
|
|
402
|
+
case 5: // string
|
|
403
|
+
row.push(api.resultCellString(r, c));
|
|
404
|
+
break;
|
|
405
|
+
case 6: // blob
|
|
406
|
+
const blobPtr = api.resultCellBlob(r, c);
|
|
407
|
+
const blobSize = api.resultCellBlobSize(r, c);
|
|
408
|
+
if (blobPtr && blobSize > 0) {
|
|
409
|
+
row.push(Array.from(new Uint8Array(Module.HEAPU8.buffer, blobPtr, blobSize)));
|
|
410
|
+
} else {
|
|
411
|
+
row.push([]);
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
default:
|
|
415
|
+
row.push(null);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
rows.push(row);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return { columns, rows };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
exportData() {
|
|
425
|
+
const ptr = api.exportData(this._handle);
|
|
426
|
+
const size = api.exportSize();
|
|
427
|
+
return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
loadAndRebuild(data) {
|
|
431
|
+
const ptr = Module._malloc(data.length);
|
|
432
|
+
Module.HEAPU8.set(data, ptr);
|
|
433
|
+
api.loadAndRebuild(this._handle, ptr, data.length);
|
|
434
|
+
Module._free(ptr);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
getStats() {
|
|
438
|
+
const count = api.getStatsCount(this._handle);
|
|
439
|
+
const stats = [];
|
|
440
|
+
for (let i = 0; i < count; i++) {
|
|
441
|
+
stats.push({
|
|
442
|
+
tableName: api.getStatTableName(i),
|
|
443
|
+
fileId: api.getStatFileId(i),
|
|
444
|
+
recordCount: api.getStatRecordCount(i)
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return stats;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
markDeleted(tableName, sequence) {
|
|
451
|
+
api.markDeleted(this._handle, tableName, sequence);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
getDeletedCount(tableName) {
|
|
455
|
+
return api.getDeletedCount(this._handle, tableName);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
clearTombstones(tableName) {
|
|
459
|
+
api.clearTombstones(this._handle, tableName);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export default initFlatSQL;
|