@h-ai/reldb 0.1.0-alpha.49 → 0.1.0-alpha.51
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/README.md +18 -0
- package/dist/index.d.ts +18 -2
- package/dist/index.js +201 -65
- package/dist/index.js.map +1 -1
- package/dist/providers/sqlite/reldb-sqlite-worker.mjs +112 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -74,6 +74,24 @@ await reldb.init({
|
|
|
74
74
|
})
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### SQLite 执行方式(worker 异步)
|
|
78
|
+
|
|
79
|
+
better-sqlite3 是同步驱动,查询会阻塞 Node 主线程事件循环。WAL 只能改善数据库锁并发,无法避免同步调用占用主线程。需要卸载这一阻塞时,可把 SQLite 切到专用 worker 线程执行(单连接,opt-in,默认 `sync`):
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await reldb.init({
|
|
83
|
+
type: 'sqlite',
|
|
84
|
+
database: './data.db',
|
|
85
|
+
sqlite: {
|
|
86
|
+
executor: 'worker', // 'sync'(默认)| 'worker'
|
|
87
|
+
walMode: true, // 默认 true
|
|
88
|
+
workerConnectTimeoutMs: 10000, // worker 打开连接超时,默认 10000
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
worker 模式下查询、写入与事务均在 worker 线程串行执行;公共 API 与返回值与同步模式完全一致。
|
|
94
|
+
|
|
77
95
|
### 操作日志
|
|
78
96
|
|
|
79
97
|
可在数据库配置中通过 `operationLog` 开启 SQL 操作日志。日志在 reldb provider-base 的 DML 操作层统一输出,不在 `reldb-main.ts` 或各 raw provider 中重复包装。
|
package/dist/index.d.ts
CHANGED
|
@@ -93,14 +93,20 @@ type SslConfig = z.infer<typeof SslConfigSchema>;
|
|
|
93
93
|
* @example
|
|
94
94
|
* ```ts
|
|
95
95
|
* sqlite: {
|
|
96
|
-
* walMode: true,
|
|
97
|
-
* readonly: false
|
|
96
|
+
* walMode: true, // 启用 WAL 模式,提高并发性能
|
|
97
|
+
* readonly: false, // 只读模式
|
|
98
|
+
* executor: 'worker' // 用 worker 线程执行,避免同步查询阻塞主线程事件循环
|
|
98
99
|
* }
|
|
99
100
|
* ```
|
|
100
101
|
*/
|
|
101
102
|
declare const SqliteOptionsSchema: z.ZodObject<{
|
|
102
103
|
walMode: z.ZodDefault<z.ZodBoolean>;
|
|
103
104
|
readonly: z.ZodDefault<z.ZodBoolean>;
|
|
105
|
+
executor: z.ZodDefault<z.ZodEnum<{
|
|
106
|
+
sync: "sync";
|
|
107
|
+
worker: "worker";
|
|
108
|
+
}>>;
|
|
109
|
+
workerConnectTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
104
110
|
}, z.core.$strip>;
|
|
105
111
|
/**
|
|
106
112
|
* MySQL 特定选项 Schema
|
|
@@ -145,6 +151,11 @@ declare const SqliteConfigSchema: z.ZodObject<{
|
|
|
145
151
|
sqlite: z.ZodOptional<z.ZodObject<{
|
|
146
152
|
walMode: z.ZodDefault<z.ZodBoolean>;
|
|
147
153
|
readonly: z.ZodDefault<z.ZodBoolean>;
|
|
154
|
+
executor: z.ZodDefault<z.ZodEnum<{
|
|
155
|
+
sync: "sync";
|
|
156
|
+
worker: "worker";
|
|
157
|
+
}>>;
|
|
158
|
+
workerConnectTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
148
159
|
}, z.core.$strip>>;
|
|
149
160
|
}, z.core.$strip>;
|
|
150
161
|
/**
|
|
@@ -274,6 +285,11 @@ declare const ReldbConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
274
285
|
sqlite: z.ZodOptional<z.ZodObject<{
|
|
275
286
|
walMode: z.ZodDefault<z.ZodBoolean>;
|
|
276
287
|
readonly: z.ZodDefault<z.ZodBoolean>;
|
|
288
|
+
executor: z.ZodDefault<z.ZodEnum<{
|
|
289
|
+
sync: "sync";
|
|
290
|
+
worker: "worker";
|
|
291
|
+
}>>;
|
|
292
|
+
workerConnectTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
277
293
|
}, z.core.$strip>>;
|
|
278
294
|
}, z.core.$strip>, z.ZodObject<{
|
|
279
295
|
url: z.ZodOptional<z.ZodString>;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { core, ok, err } from '@h-ai/core';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
3
4
|
import { createRequire } from 'module';
|
|
5
|
+
import { Worker } from 'worker_threads';
|
|
4
6
|
|
|
5
7
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
8
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -37,7 +39,17 @@ var SqliteOptionsSchema = z.object({
|
|
|
37
39
|
/** 是否启用 WAL 模式(默认 true),可提高并发读写性能 */
|
|
38
40
|
walMode: z.boolean().default(true),
|
|
39
41
|
/** 是否只读模式(默认 false) */
|
|
40
|
-
readonly: z.boolean().default(false)
|
|
42
|
+
readonly: z.boolean().default(false),
|
|
43
|
+
/**
|
|
44
|
+
* 执行方式(默认 sync)。
|
|
45
|
+
*
|
|
46
|
+
* - `sync`:主线程直接执行 better-sqlite3(同步阻塞主线程事件循环)。
|
|
47
|
+
* - `worker`:在专用 worker 线程持有唯一连接执行,把阻塞移出主线程。
|
|
48
|
+
* 注意 WAL 只改善数据库锁并发,无法避免 better-sqlite3 同步调用占用调用线程。
|
|
49
|
+
*/
|
|
50
|
+
executor: z.enum(["sync", "worker"]).default("sync"),
|
|
51
|
+
/** worker 执行方式下等待连接打开的超时(毫秒,默认 10000) */
|
|
52
|
+
workerConnectTimeoutMs: z.number().int().positive().default(1e4)
|
|
41
53
|
});
|
|
42
54
|
var MysqlOptionsSchema = z.object({
|
|
43
55
|
/** 字符集(默认 utf8mb4),推荐使用 utf8mb4 支持完整 Unicode */
|
|
@@ -2320,9 +2332,157 @@ function createPostgresProvider() {
|
|
|
2320
2332
|
};
|
|
2321
2333
|
}
|
|
2322
2334
|
var require2 = createRequire(import.meta.url);
|
|
2335
|
+
function createSyncSqliteEngine(options) {
|
|
2336
|
+
const Database = require2("better-sqlite3");
|
|
2337
|
+
const db = new Database(options.database, { readonly: options.readonly });
|
|
2338
|
+
if (options.walMode !== false) {
|
|
2339
|
+
db.pragma("journal_mode = WAL");
|
|
2340
|
+
}
|
|
2341
|
+
let open = true;
|
|
2342
|
+
return {
|
|
2343
|
+
async all(sql, params) {
|
|
2344
|
+
const stmt = db.prepare(sql);
|
|
2345
|
+
return params ? stmt.all(...params) : stmt.all();
|
|
2346
|
+
},
|
|
2347
|
+
async get(sql, params) {
|
|
2348
|
+
const stmt = db.prepare(sql);
|
|
2349
|
+
return (params ? stmt.get(...params) : stmt.get()) ?? null;
|
|
2350
|
+
},
|
|
2351
|
+
async run(sql, params) {
|
|
2352
|
+
const stmt = db.prepare(sql);
|
|
2353
|
+
const result = params ? stmt.run(...params) : stmt.run();
|
|
2354
|
+
return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
|
|
2355
|
+
},
|
|
2356
|
+
async exec(sql) {
|
|
2357
|
+
db.exec(sql);
|
|
2358
|
+
},
|
|
2359
|
+
async batch(statements) {
|
|
2360
|
+
const tx = db.transaction((items) => {
|
|
2361
|
+
for (const item of items) {
|
|
2362
|
+
const stmt = db.prepare(item.sql);
|
|
2363
|
+
if (item.params)
|
|
2364
|
+
stmt.run(...item.params);
|
|
2365
|
+
else stmt.run();
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
tx(statements);
|
|
2369
|
+
},
|
|
2370
|
+
async close() {
|
|
2371
|
+
if (open) {
|
|
2372
|
+
db.close();
|
|
2373
|
+
open = false;
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2376
|
+
isOpen: () => open
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
function resolveWorkerUrl() {
|
|
2380
|
+
const candidates = [
|
|
2381
|
+
// 源码 / 测试:引擎与 worker 同目录。
|
|
2382
|
+
new URL("./reldb-sqlite-worker.mjs", import.meta.url),
|
|
2383
|
+
// 打包:引擎被并入 dist/index.js,worker 复制到 dist/providers/sqlite/。
|
|
2384
|
+
new URL("./providers/sqlite/reldb-sqlite-worker.mjs", import.meta.url)
|
|
2385
|
+
];
|
|
2386
|
+
for (const candidate of candidates) {
|
|
2387
|
+
if (existsSync(candidate))
|
|
2388
|
+
return candidate;
|
|
2389
|
+
}
|
|
2390
|
+
return candidates[0];
|
|
2391
|
+
}
|
|
2392
|
+
async function createWorkerSqliteEngine(options, connectTimeoutMs) {
|
|
2393
|
+
const worker = new Worker(resolveWorkerUrl(), {
|
|
2394
|
+
workerData: { database: options.database, readonly: options.readonly, walMode: options.walMode }
|
|
2395
|
+
});
|
|
2396
|
+
let seq = 0;
|
|
2397
|
+
let open = true;
|
|
2398
|
+
const pending = /* @__PURE__ */ new Map();
|
|
2399
|
+
function rejectAll(error) {
|
|
2400
|
+
for (const entry of pending.values())
|
|
2401
|
+
entry.reject(error);
|
|
2402
|
+
pending.clear();
|
|
2403
|
+
}
|
|
2404
|
+
worker.on("message", (msg) => {
|
|
2405
|
+
if (!("id" in msg))
|
|
2406
|
+
return;
|
|
2407
|
+
const entry = pending.get(msg.id);
|
|
2408
|
+
if (!entry)
|
|
2409
|
+
return;
|
|
2410
|
+
pending.delete(msg.id);
|
|
2411
|
+
if (msg.ok)
|
|
2412
|
+
entry.resolve(msg.result);
|
|
2413
|
+
else entry.reject(new Error(msg.error?.message ?? "sqlite worker error"));
|
|
2414
|
+
});
|
|
2415
|
+
worker.on("error", (error) => {
|
|
2416
|
+
open = false;
|
|
2417
|
+
rejectAll(error instanceof Error ? error : new Error(String(error)));
|
|
2418
|
+
});
|
|
2419
|
+
worker.on("exit", (code) => {
|
|
2420
|
+
open = false;
|
|
2421
|
+
if (pending.size > 0)
|
|
2422
|
+
rejectAll(new Error(`sqlite worker exited unexpectedly with code ${code}`));
|
|
2423
|
+
});
|
|
2424
|
+
await new Promise((resolve, reject) => {
|
|
2425
|
+
const timer = setTimeout(() => {
|
|
2426
|
+
worker.off("message", onHandshake);
|
|
2427
|
+
reject(new Error(`sqlite worker init timed out after ${connectTimeoutMs}ms`));
|
|
2428
|
+
}, connectTimeoutMs);
|
|
2429
|
+
function onHandshake(msg) {
|
|
2430
|
+
if (msg?.type === "ready") {
|
|
2431
|
+
clearTimeout(timer);
|
|
2432
|
+
worker.off("message", onHandshake);
|
|
2433
|
+
resolve();
|
|
2434
|
+
} else if (msg?.type === "init-error") {
|
|
2435
|
+
clearTimeout(timer);
|
|
2436
|
+
worker.off("message", onHandshake);
|
|
2437
|
+
reject(new Error(msg.error?.message ?? "sqlite worker init failed"));
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
worker.on("message", onHandshake);
|
|
2441
|
+
});
|
|
2442
|
+
function post(op, payload) {
|
|
2443
|
+
if (!open)
|
|
2444
|
+
return Promise.reject(new Error("sqlite worker is not open"));
|
|
2445
|
+
const id = ++seq;
|
|
2446
|
+
return new Promise((resolve, reject) => {
|
|
2447
|
+
pending.set(id, { resolve, reject });
|
|
2448
|
+
worker.postMessage({ id, op, ...payload });
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
return {
|
|
2452
|
+
// 边界反序列化:worker 通过结构化克隆回传,行/结果类型在此断言。
|
|
2453
|
+
async all(sql, params) {
|
|
2454
|
+
return await post("all", { sql, params });
|
|
2455
|
+
},
|
|
2456
|
+
async get(sql, params) {
|
|
2457
|
+
return await post("get", { sql, params });
|
|
2458
|
+
},
|
|
2459
|
+
async run(sql, params) {
|
|
2460
|
+
return await post("run", { sql, params });
|
|
2461
|
+
},
|
|
2462
|
+
async exec(sql) {
|
|
2463
|
+
await post("exec", { sql });
|
|
2464
|
+
},
|
|
2465
|
+
async batch(statements) {
|
|
2466
|
+
await post("batch", { statements });
|
|
2467
|
+
},
|
|
2468
|
+
async close() {
|
|
2469
|
+
if (!open)
|
|
2470
|
+
return;
|
|
2471
|
+
try {
|
|
2472
|
+
await post("close", {});
|
|
2473
|
+
} catch {
|
|
2474
|
+
}
|
|
2475
|
+
open = false;
|
|
2476
|
+
await worker.terminate();
|
|
2477
|
+
},
|
|
2478
|
+
isOpen: () => open
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// src/providers/reldb-provider-sqlite.ts
|
|
2323
2483
|
var logger4 = core.logger.child({ module: "reldb", scope: "sqlite" });
|
|
2324
2484
|
function createSqliteProvider() {
|
|
2325
|
-
let
|
|
2485
|
+
let engine = null;
|
|
2326
2486
|
let currentConfig2 = null;
|
|
2327
2487
|
let txChain = Promise.resolve();
|
|
2328
2488
|
const TX_LOCK_TIMEOUT_MS = 3e4;
|
|
@@ -2350,7 +2510,7 @@ function createSqliteProvider() {
|
|
|
2350
2510
|
return release;
|
|
2351
2511
|
}
|
|
2352
2512
|
const ctx = {
|
|
2353
|
-
isConnected: () =>
|
|
2513
|
+
isConnected: () => engine !== null && engine.isOpen(),
|
|
2354
2514
|
logger: logger4,
|
|
2355
2515
|
operationLog: () => currentConfig2?.operationLog
|
|
2356
2516
|
};
|
|
@@ -2393,71 +2553,59 @@ function createSqliteProvider() {
|
|
|
2393
2553
|
async createTable(name, columns, ifNotExists = true) {
|
|
2394
2554
|
const quotedTable = quoteIdentifier(name);
|
|
2395
2555
|
const sql = buildDefaultCreateTableSql(sqliteBuildColumnSql, quotedTable, columns, ifNotExists);
|
|
2396
|
-
|
|
2556
|
+
await engine.exec(sql);
|
|
2397
2557
|
return ok(void 0);
|
|
2398
2558
|
},
|
|
2399
2559
|
async dropTable(name, ifExists) {
|
|
2400
2560
|
const ifExistsClause = ifExists ? "IF EXISTS " : "";
|
|
2401
|
-
|
|
2561
|
+
await engine.exec(`DROP TABLE ${ifExistsClause}${quoteIdentifier(name)}`);
|
|
2402
2562
|
return ok(void 0);
|
|
2403
2563
|
},
|
|
2404
2564
|
async addColumn(table, column, def) {
|
|
2405
2565
|
const colSql = sqliteBuildColumnSql(column, def);
|
|
2406
|
-
|
|
2566
|
+
await engine.exec(`ALTER TABLE ${quoteIdentifier(table)} ADD COLUMN ${colSql}`);
|
|
2407
2567
|
return ok(void 0);
|
|
2408
2568
|
},
|
|
2409
2569
|
async dropColumn(table, column) {
|
|
2410
|
-
|
|
2570
|
+
await engine.exec(`ALTER TABLE ${quoteIdentifier(table)} DROP COLUMN ${quoteIdentifier(column)}`);
|
|
2411
2571
|
return ok(void 0);
|
|
2412
2572
|
},
|
|
2413
2573
|
async renameTable(oldName, newName) {
|
|
2414
|
-
|
|
2574
|
+
await engine.exec(buildDefaultRenameTableSql(quoteIdentifier(oldName), quoteIdentifier(newName)));
|
|
2415
2575
|
return ok(void 0);
|
|
2416
2576
|
},
|
|
2417
2577
|
async createIndex(table, index, def) {
|
|
2418
2578
|
const sql = buildDefaultCreateIndexSql(quoteIdentifier, quoteIdentifier(table), quoteIdentifier(index), def);
|
|
2419
|
-
|
|
2579
|
+
await engine.exec(sql);
|
|
2420
2580
|
return ok(void 0);
|
|
2421
2581
|
},
|
|
2422
2582
|
async dropIndex(index, ifExists = true) {
|
|
2423
2583
|
const sql = buildDefaultDropIndexSql(quoteIdentifier, index, ifExists);
|
|
2424
|
-
|
|
2584
|
+
await engine.exec(sql);
|
|
2425
2585
|
return ok(void 0);
|
|
2426
2586
|
},
|
|
2427
2587
|
async raw(sql) {
|
|
2428
|
-
|
|
2588
|
+
await engine.exec(sql);
|
|
2429
2589
|
return ok(void 0);
|
|
2430
2590
|
}
|
|
2431
2591
|
};
|
|
2432
2592
|
const rawDml = {
|
|
2433
2593
|
async query(sql, params) {
|
|
2434
|
-
const
|
|
2435
|
-
const rows = params ? stmt.all(...params) : stmt.all();
|
|
2594
|
+
const rows = await engine.all(sql, params);
|
|
2436
2595
|
return ok(rows);
|
|
2437
2596
|
},
|
|
2438
2597
|
async get(sql, params) {
|
|
2439
|
-
const
|
|
2440
|
-
const row = params ? stmt.get(...params) : stmt.get();
|
|
2598
|
+
const row = await engine.get(sql, params);
|
|
2441
2599
|
return ok(row ?? null);
|
|
2442
2600
|
},
|
|
2443
2601
|
async execute(sql, params) {
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
return ok({ changes: result.changes, lastInsertRowid: result.lastInsertRowid });
|
|
2602
|
+
const result = await engine.run(sql, params);
|
|
2603
|
+
return ok(result);
|
|
2447
2604
|
},
|
|
2448
2605
|
async batch(statements) {
|
|
2449
2606
|
const releaseTxLock = await acquireTxLock();
|
|
2450
2607
|
try {
|
|
2451
|
-
|
|
2452
|
-
const transaction = db.transaction(() => {
|
|
2453
|
-
for (const { sql: s, params } of statements) {
|
|
2454
|
-
const stmt = db.prepare(s);
|
|
2455
|
-
if (params)
|
|
2456
|
-
stmt.run(...params);
|
|
2457
|
-
else stmt.run();
|
|
2458
|
-
}
|
|
2459
|
-
});
|
|
2460
|
-
transaction();
|
|
2608
|
+
await engine.batch(statements);
|
|
2461
2609
|
return ok(void 0);
|
|
2462
2610
|
} catch (error) {
|
|
2463
2611
|
return err(HaiReldbError.QUERY_FAILED, reldbM("reldb_sqliteBatchFailed", { params: { error: String(error) } }), error);
|
|
@@ -2467,45 +2615,32 @@ function createSqliteProvider() {
|
|
|
2467
2615
|
},
|
|
2468
2616
|
async queryPage(options) {
|
|
2469
2617
|
const result = await queryPageAsync(
|
|
2470
|
-
|
|
2471
|
-
const stmt = database.prepare(sqlStr);
|
|
2472
|
-
return params ? stmt.all(...params) : stmt.all();
|
|
2473
|
-
},
|
|
2618
|
+
(sqlStr, params) => engine.all(sqlStr, params),
|
|
2474
2619
|
options
|
|
2475
2620
|
);
|
|
2476
2621
|
return ok(result);
|
|
2477
2622
|
}
|
|
2478
2623
|
};
|
|
2479
|
-
function createSqliteTxDmlOps(
|
|
2624
|
+
function createSqliteTxDmlOps(txEngine) {
|
|
2480
2625
|
return {
|
|
2481
2626
|
async query(sql, params) {
|
|
2482
|
-
|
|
2483
|
-
return ok(params ? stmt.all(...params) : stmt.all());
|
|
2627
|
+
return ok(await txEngine.all(sql, params));
|
|
2484
2628
|
},
|
|
2485
2629
|
async get(sql, params) {
|
|
2486
|
-
|
|
2487
|
-
return ok((params ? stmt.get(...params) : stmt.get()) ?? null);
|
|
2630
|
+
return ok(await txEngine.get(sql, params) ?? null);
|
|
2488
2631
|
},
|
|
2489
2632
|
async execute(sql, params) {
|
|
2490
|
-
|
|
2491
|
-
const result = params ? stmt.run(...params) : stmt.run();
|
|
2492
|
-
return ok({ changes: result.changes, lastInsertRowid: result.lastInsertRowid });
|
|
2633
|
+
return ok(await txEngine.run(sql, params));
|
|
2493
2634
|
},
|
|
2494
2635
|
async batch(statements) {
|
|
2495
2636
|
for (const { sql: s, params } of statements) {
|
|
2496
|
-
|
|
2497
|
-
if (params)
|
|
2498
|
-
stmt.run(...params);
|
|
2499
|
-
else stmt.run();
|
|
2637
|
+
await txEngine.run(s, params);
|
|
2500
2638
|
}
|
|
2501
2639
|
return ok(void 0);
|
|
2502
2640
|
},
|
|
2503
2641
|
async queryPage(options) {
|
|
2504
2642
|
const result = await queryPageAsync(
|
|
2505
|
-
|
|
2506
|
-
const stmt = db.prepare(sqlStr);
|
|
2507
|
-
return params ? stmt.all(...params) : stmt.all();
|
|
2508
|
-
},
|
|
2643
|
+
(sqlStr, params) => txEngine.all(sqlStr, params),
|
|
2509
2644
|
options
|
|
2510
2645
|
);
|
|
2511
2646
|
return ok(result);
|
|
@@ -2513,7 +2648,7 @@ function createSqliteProvider() {
|
|
|
2513
2648
|
};
|
|
2514
2649
|
}
|
|
2515
2650
|
async function beginTx() {
|
|
2516
|
-
const
|
|
2651
|
+
const txEngine = engine;
|
|
2517
2652
|
let released = false;
|
|
2518
2653
|
const releaseTxLock = await acquireTxLock();
|
|
2519
2654
|
const finishTransaction = () => {
|
|
@@ -2523,19 +2658,19 @@ function createSqliteProvider() {
|
|
|
2523
2658
|
}
|
|
2524
2659
|
};
|
|
2525
2660
|
try {
|
|
2526
|
-
|
|
2661
|
+
await txEngine.exec("BEGIN TRANSACTION");
|
|
2527
2662
|
} catch (error) {
|
|
2528
2663
|
finishTransaction();
|
|
2529
2664
|
return err(HaiReldbError.TRANSACTION_FAILED, sqliteTxErrorMessage(String(error)), error);
|
|
2530
2665
|
}
|
|
2531
|
-
const txDmlOps = createSqliteTxDmlOps(
|
|
2666
|
+
const txDmlOps = createSqliteTxDmlOps(txEngine);
|
|
2532
2667
|
const guardedTxDmlOps = createBaseDmlOps(ctx, txDmlOps);
|
|
2533
2668
|
return ok(createTxHandle(guardedTxDmlOps, {
|
|
2534
2669
|
commit: async () => {
|
|
2535
|
-
|
|
2670
|
+
await txEngine.exec("COMMIT");
|
|
2536
2671
|
},
|
|
2537
2672
|
rollback: async () => {
|
|
2538
|
-
|
|
2673
|
+
await txEngine.exec("ROLLBACK");
|
|
2539
2674
|
},
|
|
2540
2675
|
release: () => finishTransaction(),
|
|
2541
2676
|
errorMessage: sqliteTxErrorMessage
|
|
@@ -2551,40 +2686,41 @@ function createSqliteProvider() {
|
|
|
2551
2686
|
return err(HaiReldbError.CONFIG_ERROR, reldbM("reldb_sqliteNeedPath"));
|
|
2552
2687
|
}
|
|
2553
2688
|
try {
|
|
2554
|
-
const Database = require2("better-sqlite3");
|
|
2555
2689
|
const sqliteOptions = {
|
|
2556
2690
|
walMode: true,
|
|
2557
2691
|
readonly: false,
|
|
2692
|
+
executor: "sync",
|
|
2693
|
+
workerConnectTimeoutMs: 1e4,
|
|
2558
2694
|
...config.sqlite ?? {}
|
|
2559
2695
|
};
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2696
|
+
const engineOptions = {
|
|
2697
|
+
database: config.database,
|
|
2698
|
+
readonly: sqliteOptions.readonly ?? false,
|
|
2699
|
+
walMode: sqliteOptions.walMode !== false
|
|
2700
|
+
};
|
|
2701
|
+
engine = sqliteOptions.executor === "worker" ? await createWorkerSqliteEngine(engineOptions, sqliteOptions.workerConnectTimeoutMs ?? 1e4) : createSyncSqliteEngine(engineOptions);
|
|
2566
2702
|
currentConfig2 = config;
|
|
2567
|
-
logger4.info("Connected to SQLite", { database: config.database });
|
|
2703
|
+
logger4.info("Connected to SQLite", { database: config.database, executor: sqliteOptions.executor });
|
|
2568
2704
|
return ok(void 0);
|
|
2569
2705
|
} catch (error) {
|
|
2570
2706
|
return err(HaiReldbError.CONNECTION_FAILED, reldbM("reldb_sqliteConnectionFailed", { params: { error: String(error) } }), error);
|
|
2571
2707
|
}
|
|
2572
2708
|
},
|
|
2573
2709
|
async close() {
|
|
2574
|
-
if (
|
|
2710
|
+
if (engine) {
|
|
2575
2711
|
try {
|
|
2576
2712
|
try {
|
|
2577
2713
|
await txChain;
|
|
2578
2714
|
} catch {
|
|
2579
2715
|
}
|
|
2580
|
-
|
|
2716
|
+
await engine.close();
|
|
2581
2717
|
} catch (error) {
|
|
2582
2718
|
currentConfig2 = null;
|
|
2583
|
-
|
|
2719
|
+
engine = null;
|
|
2584
2720
|
return err(HaiReldbError.CONNECTION_FAILED, reldbM("reldb_sqliteConnectionFailed", { params: { error: String(error) } }), error);
|
|
2585
2721
|
}
|
|
2586
2722
|
currentConfig2 = null;
|
|
2587
|
-
|
|
2723
|
+
engine = null;
|
|
2588
2724
|
logger4.info("Disconnected from SQLite");
|
|
2589
2725
|
}
|
|
2590
2726
|
return ok(void 0);
|