@dbcube/core 5.2.2 → 5.2.4
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/dist/bin.cjs +31 -20
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +33 -22
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +451 -416
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +27 -39
- package/dist/index.d.ts +27 -39
- package/dist/index.js +464 -421
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -186,8 +186,7 @@ var Downloader = class {
|
|
|
186
186
|
static currentSpinner = null;
|
|
187
187
|
static VERSION_URLS = {
|
|
188
188
|
query: "https://raw.githubusercontent.com/Dbcube/binaries/main/query-engines.json",
|
|
189
|
-
schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json"
|
|
190
|
-
sqlite: "https://raw.githubusercontent.com/Dbcube/binaries/main/sqlite-engines.json"
|
|
189
|
+
schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json"
|
|
191
190
|
};
|
|
192
191
|
/**
|
|
193
192
|
* Fetch latest version from GitHub
|
|
@@ -357,7 +356,7 @@ var Downloader = class {
|
|
|
357
356
|
spinner: "dots12"
|
|
358
357
|
}).start();
|
|
359
358
|
const binariesToProcess = [];
|
|
360
|
-
for (const prefix of ["query", "schema"
|
|
359
|
+
for (const prefix of ["query", "schema"]) {
|
|
361
360
|
try {
|
|
362
361
|
const localVersion = this.getLocalVersion(binDir, prefix);
|
|
363
362
|
const remoteVersion = await this.fetchLatestVersion(prefix);
|
|
@@ -522,34 +521,46 @@ var Downloader = class {
|
|
|
522
521
|
}
|
|
523
522
|
static extractBinary(zipPath, outputPath, prefix) {
|
|
524
523
|
return new Promise((resolve5, reject) => {
|
|
525
|
-
|
|
524
|
+
const outDir = path.dirname(outputPath);
|
|
525
|
+
let extracted = 0;
|
|
526
|
+
const pending = [];
|
|
526
527
|
fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", (entry) => {
|
|
527
|
-
if (entry.type
|
|
528
|
-
|
|
529
|
-
|
|
528
|
+
if (entry.type !== "File") {
|
|
529
|
+
entry.autodrain();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const isMain = extracted === 0;
|
|
533
|
+
extracted++;
|
|
534
|
+
const target = isMain ? outputPath : path.join(outDir, path.basename(entry.path));
|
|
535
|
+
pending.push(new Promise((res, rej) => {
|
|
536
|
+
const writeStream = fs.createWriteStream(target);
|
|
530
537
|
entry.pipe(writeStream);
|
|
531
538
|
writeStream.on("finish", () => {
|
|
532
539
|
if (process.platform !== "win32") {
|
|
533
|
-
|
|
540
|
+
try {
|
|
541
|
+
fs.chmodSync(target, 493);
|
|
542
|
+
} catch {
|
|
543
|
+
}
|
|
534
544
|
}
|
|
535
|
-
|
|
536
|
-
resolve5();
|
|
545
|
+
res();
|
|
537
546
|
});
|
|
538
|
-
writeStream.on("error",
|
|
539
|
-
|
|
540
|
-
reject(err);
|
|
541
|
-
});
|
|
542
|
-
} else {
|
|
543
|
-
entry.autodrain();
|
|
544
|
-
}
|
|
547
|
+
writeStream.on("error", rej);
|
|
548
|
+
}));
|
|
545
549
|
}).on("error", (err) => {
|
|
546
550
|
this.cleanupFile(zipPath);
|
|
547
551
|
reject(err);
|
|
548
552
|
}).on("close", () => {
|
|
549
|
-
|
|
553
|
+
Promise.all(pending).then(() => {
|
|
550
554
|
this.cleanupFile(zipPath);
|
|
551
|
-
|
|
552
|
-
|
|
555
|
+
if (extracted === 0) {
|
|
556
|
+
reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`));
|
|
557
|
+
} else {
|
|
558
|
+
resolve5();
|
|
559
|
+
}
|
|
560
|
+
}).catch((err) => {
|
|
561
|
+
this.cleanupFile(zipPath);
|
|
562
|
+
reject(err);
|
|
563
|
+
});
|
|
553
564
|
});
|
|
554
565
|
});
|
|
555
566
|
}
|
|
@@ -943,7 +954,7 @@ var DaemonClient = class _DaemonClient {
|
|
|
943
954
|
});
|
|
944
955
|
}
|
|
945
956
|
async execute(dml, txId) {
|
|
946
|
-
const payload = { action: "execute", dml
|
|
957
|
+
const payload = { action: "execute", dml };
|
|
947
958
|
if (txId) payload.tx_id = txId;
|
|
948
959
|
return this.send(payload);
|
|
949
960
|
}
|
|
@@ -1010,8 +1021,8 @@ var Engine = class {
|
|
|
1010
1021
|
const binaryPath = this.binary["query_engine"];
|
|
1011
1022
|
if (!binaryPath) return null;
|
|
1012
1023
|
const client = DaemonClient.get(this.name, binaryPath, this.arguments);
|
|
1013
|
-
const
|
|
1014
|
-
if (!
|
|
1024
|
+
const ok2 = await client.ensure();
|
|
1025
|
+
if (!ok2) {
|
|
1015
1026
|
this.daemonFailed = true;
|
|
1016
1027
|
return null;
|
|
1017
1028
|
}
|
|
@@ -1224,13 +1235,264 @@ var Engine = class {
|
|
|
1224
1235
|
|
|
1225
1236
|
// src/lib/QueryEngine.ts
|
|
1226
1237
|
var import_path5 = __toESM(require("path"));
|
|
1238
|
+
|
|
1239
|
+
// src/lib/EmbeddedEngine.ts
|
|
1240
|
+
var fs4 = __toESM(require("fs"));
|
|
1241
|
+
var path5 = __toESM(require("path"));
|
|
1242
|
+
if (!process.env.UV_THREADPOOL_SIZE) {
|
|
1243
|
+
process.env.UV_THREADPOOL_SIZE = "16";
|
|
1244
|
+
}
|
|
1245
|
+
var backend = null;
|
|
1246
|
+
var loadFailed = false;
|
|
1247
|
+
var handles = /* @__PURE__ */ new Map();
|
|
1248
|
+
var connecting = /* @__PURE__ */ new Map();
|
|
1249
|
+
function platTag() {
|
|
1250
|
+
const arch2 = process.arch === "arm64" ? "arm64" : "x64";
|
|
1251
|
+
if (process.platform === "win32") return { plat: "windows", ext: "dll", arch: arch2 };
|
|
1252
|
+
if (process.platform === "darwin") return { plat: "macos", ext: "dylib", arch: arch2 };
|
|
1253
|
+
return { plat: "linux", ext: "so", arch: arch2 };
|
|
1254
|
+
}
|
|
1255
|
+
function findFile(name) {
|
|
1256
|
+
const candidates = [
|
|
1257
|
+
path5.resolve(process.cwd(), ".dbcube", "bin", name),
|
|
1258
|
+
path5.resolve(process.cwd(), "node_modules", ".dbcube", "bin", name)
|
|
1259
|
+
];
|
|
1260
|
+
for (const c of candidates) {
|
|
1261
|
+
if (fs4.existsSync(c)) return c;
|
|
1262
|
+
}
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
function ok(data) {
|
|
1266
|
+
return { status: 200, message: "OK", data };
|
|
1267
|
+
}
|
|
1268
|
+
function errResp(e) {
|
|
1269
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1270
|
+
return { status: 500, message: msg, data: null };
|
|
1271
|
+
}
|
|
1272
|
+
function loadNapiBackend() {
|
|
1273
|
+
const { plat, arch: arch2 } = platTag();
|
|
1274
|
+
const addonPath = findFile(`query-engine-node-${plat}-${arch2}.node`);
|
|
1275
|
+
if (!addonPath) return null;
|
|
1276
|
+
try {
|
|
1277
|
+
const addon = require(addonPath);
|
|
1278
|
+
if (typeof addon.connect !== "function") return null;
|
|
1279
|
+
return {
|
|
1280
|
+
async connect(cfgJson) {
|
|
1281
|
+
return Number(await addon.connect(cfgJson));
|
|
1282
|
+
},
|
|
1283
|
+
async execute(handle, dml, txId) {
|
|
1284
|
+
try {
|
|
1285
|
+
const rows = await addon.execute(handle, dml, txId ?? void 0);
|
|
1286
|
+
return ok(rows);
|
|
1287
|
+
} catch (e) {
|
|
1288
|
+
return errResp(e);
|
|
1289
|
+
}
|
|
1290
|
+
},
|
|
1291
|
+
async executeBatch(handle, ops) {
|
|
1292
|
+
try {
|
|
1293
|
+
const results = await addon.executeBatch(handle, ops);
|
|
1294
|
+
return ok(results);
|
|
1295
|
+
} catch (e) {
|
|
1296
|
+
return errResp(e);
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
async raw(handle, query, params, txId) {
|
|
1300
|
+
try {
|
|
1301
|
+
const rows = await addon.raw(handle, query, params, txId ?? void 0);
|
|
1302
|
+
return ok(rows);
|
|
1303
|
+
} catch (e) {
|
|
1304
|
+
return errResp(e);
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
async begin(handle) {
|
|
1308
|
+
try {
|
|
1309
|
+
const txId = await addon.begin(handle);
|
|
1310
|
+
return ok({ tx_id: txId });
|
|
1311
|
+
} catch (e) {
|
|
1312
|
+
return errResp(e);
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
async commit(_handle, txId) {
|
|
1316
|
+
try {
|
|
1317
|
+
await addon.commit(txId);
|
|
1318
|
+
return ok(null);
|
|
1319
|
+
} catch (e) {
|
|
1320
|
+
return errResp(e);
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
async rollback(_handle, txId) {
|
|
1324
|
+
try {
|
|
1325
|
+
await addon.rollback(txId);
|
|
1326
|
+
return ok(null);
|
|
1327
|
+
} catch (e) {
|
|
1328
|
+
return errResp(e);
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
async disconnect(handle) {
|
|
1332
|
+
try {
|
|
1333
|
+
addon.disconnect(handle);
|
|
1334
|
+
} catch {
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
} catch {
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
function loadKoffiBackend() {
|
|
1343
|
+
const { plat, ext, arch: arch2 } = platTag();
|
|
1344
|
+
const libPath = findFile(`query-engine-embedded-${plat}-${arch2}.${ext}`);
|
|
1345
|
+
if (!libPath) return null;
|
|
1346
|
+
try {
|
|
1347
|
+
const koffi = require("koffi");
|
|
1348
|
+
const lib = koffi.load(libPath);
|
|
1349
|
+
const cConnect = lib.func("dbc_connect", "void *", ["str"]);
|
|
1350
|
+
const cExecute = lib.func("dbc_execute", "void *", ["uint64", "str", "str"]);
|
|
1351
|
+
const cExecuteBatch = lib.func("dbc_execute_batch", "void *", ["uint64", "str"]);
|
|
1352
|
+
const cRaw = lib.func("dbc_raw", "void *", ["uint64", "str", "str", "str"]);
|
|
1353
|
+
const cBegin = lib.func("dbc_begin", "void *", ["uint64"]);
|
|
1354
|
+
const cCommit = lib.func("dbc_commit", "void *", ["uint64", "str"]);
|
|
1355
|
+
const cRollback = lib.func("dbc_rollback", "void *", ["uint64", "str"]);
|
|
1356
|
+
const cDisconnect = lib.func("dbc_disconnect", "void *", ["uint64"]);
|
|
1357
|
+
const cFree = lib.func("dbc_free", "void", ["void *"]);
|
|
1358
|
+
const take = (ptr) => {
|
|
1359
|
+
if (!ptr) return { status: 500, message: "embedded engine returned null", data: null };
|
|
1360
|
+
const s = koffi.decode(ptr, "char", -1);
|
|
1361
|
+
cFree(ptr);
|
|
1362
|
+
try {
|
|
1363
|
+
const r = JSON.parse(s);
|
|
1364
|
+
return { status: r.status ?? 500, message: r.message ?? "", data: r.data ?? null };
|
|
1365
|
+
} catch (e) {
|
|
1366
|
+
return { status: 500, message: `Invalid embedded response: ${e.message}`, data: null };
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
const call = (fn, ...args) => new Promise((resolve5, reject) => {
|
|
1370
|
+
fn.async(...args, (err, ptr) => {
|
|
1371
|
+
if (err) return reject(err);
|
|
1372
|
+
resolve5(take(ptr));
|
|
1373
|
+
});
|
|
1374
|
+
});
|
|
1375
|
+
return {
|
|
1376
|
+
async connect(cfgJson) {
|
|
1377
|
+
const res = await call(cConnect, cfgJson);
|
|
1378
|
+
if (res.status !== 200 || !res.data?.handle) {
|
|
1379
|
+
throw new Error(String(res.message || "embedded connect failed"));
|
|
1380
|
+
}
|
|
1381
|
+
return Number(res.data.handle);
|
|
1382
|
+
},
|
|
1383
|
+
execute: (handle, dml, txId) => call(cExecute, handle, JSON.stringify(dml), txId),
|
|
1384
|
+
executeBatch: (handle, ops) => call(cExecuteBatch, handle, JSON.stringify(ops)),
|
|
1385
|
+
raw: (handle, query, params, txId) => call(cRaw, handle, query, JSON.stringify(params ?? []), txId),
|
|
1386
|
+
begin: (handle) => call(cBegin, handle),
|
|
1387
|
+
commit: (handle, txId) => call(cCommit, handle, txId),
|
|
1388
|
+
rollback: (handle, txId) => call(cRollback, handle, txId),
|
|
1389
|
+
async disconnect(handle) {
|
|
1390
|
+
try {
|
|
1391
|
+
await call(cDisconnect, handle);
|
|
1392
|
+
} catch {
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
} catch {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function loadBackend() {
|
|
1401
|
+
if (backend) return backend;
|
|
1402
|
+
if (loadFailed) return null;
|
|
1403
|
+
if (process.env.DBCUBE_EMBEDDED === "0" || process.env.DBCUBE_EMBEDDED === "false") {
|
|
1404
|
+
loadFailed = true;
|
|
1405
|
+
return null;
|
|
1406
|
+
}
|
|
1407
|
+
backend = loadNapiBackend() ?? loadKoffiBackend();
|
|
1408
|
+
if (!backend) loadFailed = true;
|
|
1409
|
+
return backend;
|
|
1410
|
+
}
|
|
1411
|
+
var EmbeddedEngine = class {
|
|
1412
|
+
/** true si algún backend nativo está disponible en esta instalación. */
|
|
1413
|
+
static available() {
|
|
1414
|
+
return loadBackend() !== null;
|
|
1415
|
+
}
|
|
1416
|
+
static async handleFor(connectionId, config) {
|
|
1417
|
+
const existing = handles.get(connectionId);
|
|
1418
|
+
if (existing) return existing;
|
|
1419
|
+
const inFlight = connecting.get(connectionId);
|
|
1420
|
+
if (inFlight) return inFlight;
|
|
1421
|
+
const promise = (async () => {
|
|
1422
|
+
const be = loadBackend();
|
|
1423
|
+
if (!be) throw new Error("embedded engine not available");
|
|
1424
|
+
const motor = config.type === "postgres" ? "postgresql" : config.type;
|
|
1425
|
+
const cfg = {
|
|
1426
|
+
databaseRef: connectionId,
|
|
1427
|
+
motor,
|
|
1428
|
+
database: config.type === "sqlite" ? `${config.config.DATABASE}.db` : config.config.DATABASE,
|
|
1429
|
+
host: config.config.HOST,
|
|
1430
|
+
port: config.config.PORT != null ? Number(config.config.PORT) : void 0,
|
|
1431
|
+
user: config.config.USER,
|
|
1432
|
+
password: config.config.PASSWORD,
|
|
1433
|
+
maxConnections: config.pool?.maxConnections,
|
|
1434
|
+
minConnections: config.pool?.minConnections,
|
|
1435
|
+
acquireTimeoutMs: config.pool?.acquireTimeoutMs,
|
|
1436
|
+
idleTimeoutMs: config.pool?.idleTimeoutMs
|
|
1437
|
+
};
|
|
1438
|
+
const handle = await be.connect(JSON.stringify(cfg));
|
|
1439
|
+
handles.set(connectionId, handle);
|
|
1440
|
+
return handle;
|
|
1441
|
+
})();
|
|
1442
|
+
connecting.set(connectionId, promise);
|
|
1443
|
+
try {
|
|
1444
|
+
return await promise;
|
|
1445
|
+
} finally {
|
|
1446
|
+
connecting.delete(connectionId);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
static async executeDml(connectionId, config, dml, txId) {
|
|
1450
|
+
const be = loadBackend();
|
|
1451
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1452
|
+
return be.execute(handle, dml, txId ?? null);
|
|
1453
|
+
}
|
|
1454
|
+
static async executeBatch(connectionId, config, ops) {
|
|
1455
|
+
const be = loadBackend();
|
|
1456
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1457
|
+
return be.executeBatch(handle, ops);
|
|
1458
|
+
}
|
|
1459
|
+
static async rawQuery(connectionId, config, query, params, txId) {
|
|
1460
|
+
const be = loadBackend();
|
|
1461
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1462
|
+
return be.raw(handle, query, params ?? [], txId ?? null);
|
|
1463
|
+
}
|
|
1464
|
+
static async begin(connectionId, config) {
|
|
1465
|
+
const be = loadBackend();
|
|
1466
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1467
|
+
return be.begin(handle);
|
|
1468
|
+
}
|
|
1469
|
+
static async commit(connectionId, config, txId) {
|
|
1470
|
+
const be = loadBackend();
|
|
1471
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1472
|
+
return be.commit(handle, txId);
|
|
1473
|
+
}
|
|
1474
|
+
static async rollback(connectionId, config, txId) {
|
|
1475
|
+
const be = loadBackend();
|
|
1476
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1477
|
+
return be.rollback(handle, txId);
|
|
1478
|
+
}
|
|
1479
|
+
static async disconnect(connectionId) {
|
|
1480
|
+
const be = loadBackend();
|
|
1481
|
+
const handle = handles.get(connectionId);
|
|
1482
|
+
if (be && handle) {
|
|
1483
|
+
handles.delete(connectionId);
|
|
1484
|
+
await be.disconnect(handle);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
|
|
1489
|
+
// src/lib/QueryEngine.ts
|
|
1227
1490
|
var import_module2 = require("module");
|
|
1228
1491
|
var net2 = __toESM(require("net"));
|
|
1229
1492
|
var import_child_process3 = require("child_process");
|
|
1230
1493
|
var globalTcpServers = /* @__PURE__ */ new Map();
|
|
1231
|
-
var
|
|
1232
|
-
var
|
|
1233
|
-
var connectionProcessing = /* @__PURE__ */ new Map();
|
|
1494
|
+
var socketPools = /* @__PURE__ */ new Map();
|
|
1495
|
+
var CLIENT_POOL_SIZE = 10;
|
|
1234
1496
|
var queryCache = /* @__PURE__ */ new Map();
|
|
1235
1497
|
var MAX_CACHE_SIZE = 500;
|
|
1236
1498
|
var QueryEngine = class {
|
|
@@ -1241,9 +1503,9 @@ var QueryEngine = class {
|
|
|
1241
1503
|
timeout;
|
|
1242
1504
|
connectionId;
|
|
1243
1505
|
tcpPort;
|
|
1244
|
-
constructor(name, timeout = 3e4) {
|
|
1506
|
+
constructor(name, timeout = 3e4, explicitConfig) {
|
|
1245
1507
|
this.name = name;
|
|
1246
|
-
this.config = this.setConfig(name);
|
|
1508
|
+
this.config = explicitConfig ?? this.setConfig(name);
|
|
1247
1509
|
this.arguments = this.setArguments();
|
|
1248
1510
|
this.timeout = this.config?.daemon?.requestTimeoutMs ?? timeout;
|
|
1249
1511
|
this.connectionId = `${name}_query_engine_${this.config.type}_${this.config.config.DATABASE}_${this.config.config.HOST || "localhost"}`;
|
|
@@ -1373,38 +1635,79 @@ var QueryEngine = class {
|
|
|
1373
1635
|
}
|
|
1374
1636
|
return command;
|
|
1375
1637
|
}
|
|
1638
|
+
/** El motor embebido (FFI) es el camino por defecto cuando la librería
|
|
1639
|
+
* nativa está disponible; el daemon TCP queda como fallback y como modo
|
|
1640
|
+
* multi-proceso explícito. */
|
|
1641
|
+
useEmbedded() {
|
|
1642
|
+
return EmbeddedEngine.available();
|
|
1643
|
+
}
|
|
1376
1644
|
/**
|
|
1377
|
-
* Executes a DML plan
|
|
1645
|
+
* Executes a DML plan. Embedded engine when available (in-process FFI,
|
|
1646
|
+
* no network hop); persistent TCP daemon otherwise.
|
|
1378
1647
|
* Pass txId to run it inside an active transaction.
|
|
1379
1648
|
*/
|
|
1380
1649
|
async executeDml(dml, txId) {
|
|
1381
|
-
|
|
1650
|
+
if (this.useEmbedded()) {
|
|
1651
|
+
return EmbeddedEngine.executeDml(this.connectionId, this.config, dml, txId);
|
|
1652
|
+
}
|
|
1653
|
+
const command = { action: "execute", dml };
|
|
1382
1654
|
if (txId) command.tx_id = txId;
|
|
1383
1655
|
return this.executeWithTcpServer(command);
|
|
1384
1656
|
}
|
|
1385
1657
|
/**
|
|
1386
|
-
*
|
|
1387
|
-
* over the
|
|
1658
|
+
* Atomic batch: N write plans inside one transaction with a single
|
|
1659
|
+
* engine round-trip. Falls back to begin/ops/commit over the daemon
|
|
1660
|
+
* when the embedded engine is not available.
|
|
1661
|
+
*/
|
|
1662
|
+
async executeBatch(ops) {
|
|
1663
|
+
if (this.useEmbedded()) {
|
|
1664
|
+
return EmbeddedEngine.executeBatch(this.connectionId, this.config, ops);
|
|
1665
|
+
}
|
|
1666
|
+
const txId = await this.beginTransaction();
|
|
1667
|
+
try {
|
|
1668
|
+
const results = [];
|
|
1669
|
+
for (const op of ops) {
|
|
1670
|
+
const res = await this.executeDml(op, txId);
|
|
1671
|
+
if (res.status !== 200) {
|
|
1672
|
+
throw new Error(String(res.message));
|
|
1673
|
+
}
|
|
1674
|
+
results.push(res.data);
|
|
1675
|
+
}
|
|
1676
|
+
await this.commitTransaction(txId);
|
|
1677
|
+
return { status: 200, message: "Batch committed", data: results };
|
|
1678
|
+
} catch (error) {
|
|
1679
|
+
try {
|
|
1680
|
+
await this.rollbackTransaction(txId);
|
|
1681
|
+
} catch {
|
|
1682
|
+
}
|
|
1683
|
+
return { status: 500, message: error?.message ?? String(error), data: null };
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Executes raw SQL (or a MongoDB command document) with bound parameters.
|
|
1388
1688
|
*/
|
|
1389
1689
|
async rawQuery(query, params = [], txId) {
|
|
1690
|
+
if (this.useEmbedded()) {
|
|
1691
|
+
return EmbeddedEngine.rawQuery(this.connectionId, this.config, query, params, txId);
|
|
1692
|
+
}
|
|
1390
1693
|
const command = { action: "raw", query, params };
|
|
1391
1694
|
if (txId) command.tx_id = txId;
|
|
1392
1695
|
return this.executeWithTcpServer(command);
|
|
1393
1696
|
}
|
|
1394
|
-
/** Starts a
|
|
1697
|
+
/** Starts a transaction and returns its id. */
|
|
1395
1698
|
async beginTransaction() {
|
|
1396
|
-
const res = await this.executeWithTcpServer({ action: "begin" });
|
|
1699
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.begin(this.connectionId, this.config) : await this.executeWithTcpServer({ action: "begin" });
|
|
1397
1700
|
if (res.status !== 200 || !res.data?.tx_id) {
|
|
1398
1701
|
throw new Error(String(res.message || "Failed to begin transaction (update the query-engine binary: npx dbcube update)"));
|
|
1399
1702
|
}
|
|
1400
1703
|
return res.data.tx_id;
|
|
1401
1704
|
}
|
|
1402
1705
|
async commitTransaction(txId) {
|
|
1403
|
-
const res = await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1706
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.commit(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1404
1707
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to commit transaction"));
|
|
1405
1708
|
}
|
|
1406
1709
|
async rollbackTransaction(txId) {
|
|
1407
|
-
const res = await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1710
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.rollback(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1408
1711
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to rollback transaction"));
|
|
1409
1712
|
}
|
|
1410
1713
|
async executeWithTcpServer(command) {
|
|
@@ -1423,7 +1726,63 @@ var QueryEngine = class {
|
|
|
1423
1726
|
await this.startTcpServer();
|
|
1424
1727
|
await this.waitForServerReady();
|
|
1425
1728
|
}
|
|
1426
|
-
|
|
1729
|
+
const port = globalTcpServers.get(this.connectionId).port;
|
|
1730
|
+
const socket = await this.acquireSocket(port);
|
|
1731
|
+
try {
|
|
1732
|
+
const result = await this.executeOnConnection(socket, command);
|
|
1733
|
+
this.releaseSocket(socket, false);
|
|
1734
|
+
return result;
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
this.releaseSocket(socket, true);
|
|
1737
|
+
throw error;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
poolFor() {
|
|
1741
|
+
let pool = socketPools.get(this.connectionId);
|
|
1742
|
+
if (!pool) {
|
|
1743
|
+
pool = { free: [], total: 0, waiters: [] };
|
|
1744
|
+
socketPools.set(this.connectionId, pool);
|
|
1745
|
+
}
|
|
1746
|
+
return pool;
|
|
1747
|
+
}
|
|
1748
|
+
async acquireSocket(port) {
|
|
1749
|
+
const pool = this.poolFor();
|
|
1750
|
+
while (pool.free.length > 0) {
|
|
1751
|
+
const s = pool.free.pop();
|
|
1752
|
+
if (!s.destroyed) return s;
|
|
1753
|
+
pool.total--;
|
|
1754
|
+
}
|
|
1755
|
+
if (pool.total < CLIENT_POOL_SIZE) {
|
|
1756
|
+
pool.total++;
|
|
1757
|
+
try {
|
|
1758
|
+
return await this.createNewConnection(port);
|
|
1759
|
+
} catch (e) {
|
|
1760
|
+
pool.total--;
|
|
1761
|
+
throw e;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return new Promise((resolve5) => pool.waiters.push(resolve5));
|
|
1765
|
+
}
|
|
1766
|
+
releaseSocket(socket, broken) {
|
|
1767
|
+
const pool = socketPools.get(this.connectionId);
|
|
1768
|
+
if (!pool) {
|
|
1769
|
+
socket.destroy();
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
if (broken || socket.destroyed) {
|
|
1773
|
+
pool.total--;
|
|
1774
|
+
try {
|
|
1775
|
+
socket.destroy();
|
|
1776
|
+
} catch {
|
|
1777
|
+
}
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
const waiter = pool.waiters.shift();
|
|
1781
|
+
if (waiter) {
|
|
1782
|
+
waiter(socket);
|
|
1783
|
+
} else {
|
|
1784
|
+
pool.free.push(socket);
|
|
1785
|
+
}
|
|
1427
1786
|
}
|
|
1428
1787
|
async waitForServerReady() {
|
|
1429
1788
|
const maxRetries = 10;
|
|
@@ -1517,58 +1876,6 @@ var QueryEngine = class {
|
|
|
1517
1876
|
});
|
|
1518
1877
|
});
|
|
1519
1878
|
}
|
|
1520
|
-
async sendTcpRequestFast(command) {
|
|
1521
|
-
return new Promise((resolve5, reject) => {
|
|
1522
|
-
let queue = connectionQueues.get(this.connectionId);
|
|
1523
|
-
if (!queue) {
|
|
1524
|
-
queue = [];
|
|
1525
|
-
connectionQueues.set(this.connectionId, queue);
|
|
1526
|
-
}
|
|
1527
|
-
queue.push({ command, resolve: resolve5, reject });
|
|
1528
|
-
this.processQueue();
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
async processQueue() {
|
|
1532
|
-
if (connectionProcessing.get(this.connectionId)) {
|
|
1533
|
-
return;
|
|
1534
|
-
}
|
|
1535
|
-
const queue = connectionQueues.get(this.connectionId);
|
|
1536
|
-
if (!queue || queue.length === 0) {
|
|
1537
|
-
return;
|
|
1538
|
-
}
|
|
1539
|
-
connectionProcessing.set(this.connectionId, true);
|
|
1540
|
-
try {
|
|
1541
|
-
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1542
|
-
if (!serverInfo) {
|
|
1543
|
-
throw new Error("Server not initialized");
|
|
1544
|
-
}
|
|
1545
|
-
while (queue.length > 0) {
|
|
1546
|
-
const request = queue.shift();
|
|
1547
|
-
if (!request) break;
|
|
1548
|
-
try {
|
|
1549
|
-
let connection = globalTcpConnections.get(this.connectionId);
|
|
1550
|
-
if (!connection || connection.destroyed || connection.readyState !== "open") {
|
|
1551
|
-
connection = await this.createNewConnection(serverInfo.port);
|
|
1552
|
-
globalTcpConnections.set(this.connectionId, connection);
|
|
1553
|
-
}
|
|
1554
|
-
const result = await this.executeOnConnection(connection, request.command);
|
|
1555
|
-
request.resolve(result);
|
|
1556
|
-
} catch (error) {
|
|
1557
|
-
const staleConnection = globalTcpConnections.get(this.connectionId);
|
|
1558
|
-
if (staleConnection) {
|
|
1559
|
-
try {
|
|
1560
|
-
staleConnection.destroy();
|
|
1561
|
-
} catch {
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
globalTcpConnections.delete(this.connectionId);
|
|
1565
|
-
request.reject(error);
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
} finally {
|
|
1569
|
-
connectionProcessing.set(this.connectionId, false);
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
1879
|
async createNewConnection(port) {
|
|
1573
1880
|
return new Promise((resolve5, reject) => {
|
|
1574
1881
|
const client = new net2.Socket();
|
|
@@ -1716,11 +2023,16 @@ var QueryEngine = class {
|
|
|
1716
2023
|
});
|
|
1717
2024
|
}
|
|
1718
2025
|
async disconnect() {
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
2026
|
+
await EmbeddedEngine.disconnect(this.connectionId);
|
|
2027
|
+
const pool = socketPools.get(this.connectionId);
|
|
2028
|
+
if (pool) {
|
|
2029
|
+
for (const s of pool.free) {
|
|
2030
|
+
try {
|
|
2031
|
+
s.destroy();
|
|
2032
|
+
} catch {
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
socketPools.delete(this.connectionId);
|
|
1724
2036
|
}
|
|
1725
2037
|
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1726
2038
|
if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
|
|
@@ -1740,347 +2052,70 @@ var QueryEngine = class {
|
|
|
1740
2052
|
}
|
|
1741
2053
|
};
|
|
1742
2054
|
|
|
1743
|
-
// src/lib/SqliteExecutor.ts
|
|
1744
|
-
var import_child_process4 = require("child_process");
|
|
1745
|
-
var path6 = __toESM(require("path"));
|
|
1746
|
-
var fs4 = __toESM(require("fs"));
|
|
1747
|
-
var import_util = require("util");
|
|
1748
|
-
var import_module3 = require("module");
|
|
1749
|
-
var import_url3 = require("url");
|
|
1750
|
-
var import_path6 = require("path");
|
|
1751
|
-
var import_meta3 = {};
|
|
1752
|
-
var execAsync = (0, import_util.promisify)(import_child_process4.exec);
|
|
1753
|
-
var SqliteExecutor = class {
|
|
1754
|
-
binaryPath;
|
|
1755
|
-
dbPath;
|
|
1756
|
-
constructor(dbPath) {
|
|
1757
|
-
this.dbPath = dbPath;
|
|
1758
|
-
this.binaryPath = this.getBinaryPath();
|
|
1759
|
-
}
|
|
1760
|
-
findVersionedBinary(binDir, platform2) {
|
|
1761
|
-
try {
|
|
1762
|
-
const files = fs4.readdirSync(binDir);
|
|
1763
|
-
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1764
|
-
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1765
|
-
const pattern = new RegExp(`^sqlite-engine-v\\d+\\.\\d+\\.\\d+-${platformName}-x64${extension.replace(".", "\\.")}$`);
|
|
1766
|
-
const matchingFile = files.find((f) => pattern.test(f));
|
|
1767
|
-
if (matchingFile) {
|
|
1768
|
-
return path6.join(binDir, matchingFile);
|
|
1769
|
-
}
|
|
1770
|
-
} catch (error) {
|
|
1771
|
-
}
|
|
1772
|
-
return null;
|
|
1773
|
-
}
|
|
1774
|
-
getBinaryPath() {
|
|
1775
|
-
const __filename2 = typeof import_meta3 !== "undefined" && import_meta3.url ? (0, import_url3.fileURLToPath)(import_meta3.url) : "";
|
|
1776
|
-
const __dirname = __filename2 ? (0, import_path6.dirname)(__filename2) : process.cwd();
|
|
1777
|
-
const possibleDirs = [
|
|
1778
|
-
path6.resolve(process.cwd(), ".dbcube", "bin"),
|
|
1779
|
-
path6.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
|
|
1780
|
-
path6.resolve(__dirname, "..", "bin")
|
|
1781
|
-
];
|
|
1782
|
-
const platform2 = process.platform;
|
|
1783
|
-
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1784
|
-
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1785
|
-
for (const dir of possibleDirs) {
|
|
1786
|
-
const versionedPath = this.findVersionedBinary(dir, platform2);
|
|
1787
|
-
if (versionedPath && fs4.existsSync(versionedPath)) {
|
|
1788
|
-
return versionedPath;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
const binaryName = `sqlite-engine-${platformName}-x64${extension}`;
|
|
1792
|
-
for (const dir of possibleDirs) {
|
|
1793
|
-
const fullPath = path6.join(dir, binaryName);
|
|
1794
|
-
if (fs4.existsSync(fullPath)) {
|
|
1795
|
-
return fullPath;
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
const fallbackName = `sqlite-engine${extension}`;
|
|
1799
|
-
for (const dir of possibleDirs) {
|
|
1800
|
-
const fullPath = path6.join(dir, fallbackName);
|
|
1801
|
-
if (fs4.existsSync(fullPath)) {
|
|
1802
|
-
return fullPath;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
return path6.join(possibleDirs[0], binaryName);
|
|
1806
|
-
}
|
|
1807
|
-
async executeBinary(args) {
|
|
1808
|
-
const escapedArgs = args.map((arg) => {
|
|
1809
|
-
if (arg.includes(" ") || arg.includes('"') || arg.includes("(") || arg.includes(")")) {
|
|
1810
|
-
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
1811
|
-
}
|
|
1812
|
-
return arg;
|
|
1813
|
-
});
|
|
1814
|
-
const command = `"${this.binaryPath}" ${escapedArgs.join(" ")}`;
|
|
1815
|
-
try {
|
|
1816
|
-
const { stdout, stderr } = await execAsync(command, {
|
|
1817
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
1818
|
-
// 10MB buffer
|
|
1819
|
-
timeout: 3e4
|
|
1820
|
-
// 30s timeout
|
|
1821
|
-
});
|
|
1822
|
-
if (stderr && stderr.trim()) {
|
|
1823
|
-
console.warn("SQLite Engine Warning:", stderr);
|
|
1824
|
-
}
|
|
1825
|
-
const result = JSON.parse(stdout.trim());
|
|
1826
|
-
return result;
|
|
1827
|
-
} catch (error) {
|
|
1828
|
-
if (error.stdout) {
|
|
1829
|
-
try {
|
|
1830
|
-
const result = JSON.parse(error.stdout.trim());
|
|
1831
|
-
return result;
|
|
1832
|
-
} catch (parseError) {
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
throw new Error(`SQLite execution failed: ${error.message}`);
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
async connect() {
|
|
1839
|
-
try {
|
|
1840
|
-
const result = await this.executeBinary([
|
|
1841
|
-
"--action",
|
|
1842
|
-
"connect",
|
|
1843
|
-
"--database",
|
|
1844
|
-
this.dbPath
|
|
1845
|
-
]);
|
|
1846
|
-
return result.status === "success";
|
|
1847
|
-
} catch (error) {
|
|
1848
|
-
return false;
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
async exists() {
|
|
1852
|
-
try {
|
|
1853
|
-
const result = await this.executeBinary([
|
|
1854
|
-
"--action",
|
|
1855
|
-
"exists",
|
|
1856
|
-
"--database",
|
|
1857
|
-
this.dbPath
|
|
1858
|
-
]);
|
|
1859
|
-
return result.status === "success" && result.data === true;
|
|
1860
|
-
} catch (error) {
|
|
1861
|
-
return false;
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
async query(sql, params) {
|
|
1865
|
-
const args = [
|
|
1866
|
-
"--action",
|
|
1867
|
-
"query",
|
|
1868
|
-
"--database",
|
|
1869
|
-
this.dbPath,
|
|
1870
|
-
"--query",
|
|
1871
|
-
sql
|
|
1872
|
-
];
|
|
1873
|
-
if (params && params.length > 0) {
|
|
1874
|
-
args.push("--params", JSON.stringify(params));
|
|
1875
|
-
}
|
|
1876
|
-
return this.executeBinary(args);
|
|
1877
|
-
}
|
|
1878
|
-
// Método para múltiples queries (como lo hace better-sqlite3)
|
|
1879
|
-
async queryMultiple(sql) {
|
|
1880
|
-
return this.query(sql);
|
|
1881
|
-
}
|
|
1882
|
-
// Método prepare que simula prepared statements
|
|
1883
|
-
prepare(sql) {
|
|
1884
|
-
return {
|
|
1885
|
-
all: async (...params) => {
|
|
1886
|
-
const result = await this.query(sql, params);
|
|
1887
|
-
if (result.status === "error") {
|
|
1888
|
-
throw new Error(result.message);
|
|
1889
|
-
}
|
|
1890
|
-
return Array.isArray(result.data) ? result.data : [];
|
|
1891
|
-
},
|
|
1892
|
-
run: async (...params) => {
|
|
1893
|
-
const result = await this.query(sql, params);
|
|
1894
|
-
if (result.status === "error") {
|
|
1895
|
-
throw new Error(result.message);
|
|
1896
|
-
}
|
|
1897
|
-
if (typeof result.data === "object" && result.data.hasOwnProperty("changes") && result.data.hasOwnProperty("lastID")) {
|
|
1898
|
-
return result.data;
|
|
1899
|
-
}
|
|
1900
|
-
return { changes: 0, lastID: 0 };
|
|
1901
|
-
}
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
// Para compatibilidad con better-sqlite3 API sincrona usando deasync si es necesario
|
|
1905
|
-
prepareSync(sql) {
|
|
1906
|
-
const __filename2 = typeof import_meta3 !== "undefined" && import_meta3.url ? (0, import_url3.fileURLToPath)(import_meta3.url) : "";
|
|
1907
|
-
const requireUrl = __filename2 || import_meta3.url;
|
|
1908
|
-
const require2 = (0, import_module3.createRequire)(requireUrl);
|
|
1909
|
-
const deasync = require2("deasync");
|
|
1910
|
-
return {
|
|
1911
|
-
all: (...params) => {
|
|
1912
|
-
let result;
|
|
1913
|
-
let done = false;
|
|
1914
|
-
let error;
|
|
1915
|
-
this.query(sql, params).then((res) => {
|
|
1916
|
-
if (res.status === "error") {
|
|
1917
|
-
error = new Error(res.message);
|
|
1918
|
-
} else {
|
|
1919
|
-
result = Array.isArray(res.data) ? res.data : [];
|
|
1920
|
-
}
|
|
1921
|
-
done = true;
|
|
1922
|
-
}).catch((err) => {
|
|
1923
|
-
error = err;
|
|
1924
|
-
done = true;
|
|
1925
|
-
});
|
|
1926
|
-
deasync.loopWhile(() => !done);
|
|
1927
|
-
if (error) throw error;
|
|
1928
|
-
return result;
|
|
1929
|
-
},
|
|
1930
|
-
run: (...params) => {
|
|
1931
|
-
let result;
|
|
1932
|
-
let done = false;
|
|
1933
|
-
let error;
|
|
1934
|
-
this.query(sql, params).then((res) => {
|
|
1935
|
-
if (res.status === "error") {
|
|
1936
|
-
error = new Error(res.message);
|
|
1937
|
-
} else if (typeof res.data === "object" && res.data.hasOwnProperty("changes") && res.data.hasOwnProperty("lastID")) {
|
|
1938
|
-
result = res.data;
|
|
1939
|
-
} else {
|
|
1940
|
-
result = { changes: 0, lastID: 0 };
|
|
1941
|
-
}
|
|
1942
|
-
done = true;
|
|
1943
|
-
}).catch((err) => {
|
|
1944
|
-
error = err;
|
|
1945
|
-
done = true;
|
|
1946
|
-
});
|
|
1947
|
-
deasync.loopWhile(() => !done);
|
|
1948
|
-
if (error) throw error;
|
|
1949
|
-
return result;
|
|
1950
|
-
}
|
|
1951
|
-
};
|
|
1952
|
-
}
|
|
1953
|
-
};
|
|
1954
|
-
|
|
1955
2055
|
// src/lib/DbConfig.ts
|
|
1956
2056
|
var path7 = __toESM(require("path"));
|
|
1957
2057
|
var import_fs2 = __toESM(require("fs"));
|
|
1958
2058
|
var rootPath = path7.resolve(process.cwd(), ".dbcube");
|
|
1959
2059
|
var SQLite = class {
|
|
1960
|
-
executor = null;
|
|
1961
2060
|
database;
|
|
2061
|
+
engine = null;
|
|
1962
2062
|
constructor(config) {
|
|
1963
|
-
this.database = config.DATABASE;
|
|
2063
|
+
this.database = config.DATABASE || "config";
|
|
2064
|
+
}
|
|
2065
|
+
/** Ruta del archivo SQLite interno (.dbcube/<database>.db). */
|
|
2066
|
+
dbFilePath() {
|
|
2067
|
+
return path7.join(rootPath, this.database + ".db");
|
|
2068
|
+
}
|
|
2069
|
+
/** QueryEngine sqlite apuntando al archivo interno, con config explícito
|
|
2070
|
+
* (no vive en dbcube.config.js). La ruta relativa `.dbcube/<db>` deja que
|
|
2071
|
+
* QueryEngine le añada `.db` → `.dbcube/<db>.db`, relativo al CWD. */
|
|
2072
|
+
getEngine() {
|
|
2073
|
+
if (!this.engine) {
|
|
2074
|
+
const relativeDb = path7.join(".dbcube", this.database).replace(/\\/g, "/");
|
|
2075
|
+
this.engine = new QueryEngine(`dbcube-internal-${this.database}`, 3e4, {
|
|
2076
|
+
type: "sqlite",
|
|
2077
|
+
config: { DATABASE: relativeDb }
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
return this.engine;
|
|
1964
2081
|
}
|
|
1965
2082
|
async ifExist() {
|
|
1966
|
-
if (
|
|
1967
|
-
|
|
1968
|
-
const configPath = path7.join(rootPath, dbPath + ".db");
|
|
1969
|
-
if (!import_fs2.default.existsSync(rootPath)) {
|
|
1970
|
-
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
1971
|
-
}
|
|
1972
|
-
if (import_fs2.default.existsSync(configPath)) {
|
|
1973
|
-
return true;
|
|
1974
|
-
}
|
|
1975
|
-
if (!this.executor) {
|
|
1976
|
-
this.executor = new SqliteExecutor(configPath);
|
|
1977
|
-
}
|
|
1978
|
-
return await this.executor.exists();
|
|
2083
|
+
if (!import_fs2.default.existsSync(rootPath)) {
|
|
2084
|
+
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
1979
2085
|
}
|
|
1980
|
-
return
|
|
2086
|
+
return import_fs2.default.existsSync(this.dbFilePath());
|
|
1981
2087
|
}
|
|
1982
2088
|
async connect() {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
const configPath = path7.join(rootPath, dbPath + ".db");
|
|
1988
|
-
if (!import_fs2.default.existsSync(rootPath)) {
|
|
1989
|
-
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
1990
|
-
}
|
|
1991
|
-
this.executor = new SqliteExecutor(configPath);
|
|
1992
|
-
const connected = await this.executor.connect();
|
|
1993
|
-
if (!connected) {
|
|
1994
|
-
throw new Error("Failed to connect to SQLite database");
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
resolve5(this.executor);
|
|
1998
|
-
} catch (error) {
|
|
1999
|
-
reject(error);
|
|
2000
|
-
}
|
|
2001
|
-
});
|
|
2089
|
+
if (!import_fs2.default.existsSync(rootPath)) {
|
|
2090
|
+
import_fs2.default.mkdirSync(rootPath, { recursive: true });
|
|
2091
|
+
}
|
|
2092
|
+
return this.getEngine();
|
|
2002
2093
|
}
|
|
2003
2094
|
async disconnect() {
|
|
2004
|
-
return new Promise((resolve5) => {
|
|
2005
|
-
if (this.executor) {
|
|
2006
|
-
this.executor = null;
|
|
2007
|
-
}
|
|
2008
|
-
resolve5();
|
|
2009
|
-
});
|
|
2010
2095
|
}
|
|
2011
2096
|
async query(sqlQuery) {
|
|
2012
|
-
return
|
|
2013
|
-
try {
|
|
2014
|
-
if (typeof sqlQuery !== "string") {
|
|
2015
|
-
throw new Error("The SQL query must be a string.");
|
|
2016
|
-
}
|
|
2017
|
-
if (!this.executor) {
|
|
2018
|
-
await this.connect();
|
|
2019
|
-
}
|
|
2020
|
-
if (!this.executor) {
|
|
2021
|
-
throw new Error("Database connection is not available.");
|
|
2022
|
-
}
|
|
2023
|
-
const result = await this.executor.queryMultiple(sqlQuery);
|
|
2024
|
-
if (result.status === "error") {
|
|
2025
|
-
resolve5({
|
|
2026
|
-
status: "error",
|
|
2027
|
-
message: result.message,
|
|
2028
|
-
data: null
|
|
2029
|
-
});
|
|
2030
|
-
} else {
|
|
2031
|
-
resolve5({
|
|
2032
|
-
status: "success",
|
|
2033
|
-
message: "Query executed successfully",
|
|
2034
|
-
data: result.data
|
|
2035
|
-
});
|
|
2036
|
-
}
|
|
2037
|
-
} catch (error) {
|
|
2038
|
-
resolve5({
|
|
2039
|
-
status: "error",
|
|
2040
|
-
message: error.message || "An error occurred while executing the query.",
|
|
2041
|
-
data: null
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
});
|
|
2097
|
+
return this.queryWithParameters(sqlQuery, []);
|
|
2045
2098
|
}
|
|
2046
2099
|
async queryWithParameters(sqlQuery, params = []) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
throw new Error("The SQL query must be a string.");
|
|
2051
|
-
}
|
|
2052
|
-
if (!Array.isArray(params)) {
|
|
2053
|
-
throw new Error("Parameters must be an array.");
|
|
2054
|
-
}
|
|
2055
|
-
if (!this.executor) {
|
|
2056
|
-
await this.connect();
|
|
2057
|
-
}
|
|
2058
|
-
if (!this.executor) {
|
|
2059
|
-
throw new Error("Database connection is not available.");
|
|
2060
|
-
}
|
|
2061
|
-
const result = await this.executor.query(sqlQuery, params);
|
|
2062
|
-
if (result.status === "error") {
|
|
2063
|
-
resolve5({
|
|
2064
|
-
status: "error",
|
|
2065
|
-
message: result.message,
|
|
2066
|
-
data: null
|
|
2067
|
-
});
|
|
2068
|
-
} else {
|
|
2069
|
-
resolve5({
|
|
2070
|
-
status: "success",
|
|
2071
|
-
message: "Query executed successfully",
|
|
2072
|
-
data: result.data
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
} catch (error) {
|
|
2076
|
-
console.log(error);
|
|
2077
|
-
resolve5({
|
|
2078
|
-
status: "error",
|
|
2079
|
-
message: error.message || "An error occurred while executing the query.",
|
|
2080
|
-
data: null
|
|
2081
|
-
});
|
|
2100
|
+
try {
|
|
2101
|
+
if (typeof sqlQuery !== "string") {
|
|
2102
|
+
throw new Error("The SQL query must be a string.");
|
|
2082
2103
|
}
|
|
2083
|
-
|
|
2104
|
+
if (!Array.isArray(params)) {
|
|
2105
|
+
throw new Error("Parameters must be an array.");
|
|
2106
|
+
}
|
|
2107
|
+
const response = await this.getEngine().rawQuery(sqlQuery, params);
|
|
2108
|
+
if (response.status !== 200) {
|
|
2109
|
+
return { status: "error", message: response.message || "Query failed", data: null };
|
|
2110
|
+
}
|
|
2111
|
+
return { status: "success", message: "Query executed successfully", data: response.data };
|
|
2112
|
+
} catch (error) {
|
|
2113
|
+
return {
|
|
2114
|
+
status: "error",
|
|
2115
|
+
message: error.message || "An error occurred while executing the query.",
|
|
2116
|
+
data: null
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2084
2119
|
}
|
|
2085
2120
|
convertToParameterizedQuery(sql) {
|
|
2086
2121
|
const normalizedSql = sql.replace(/\s+/g, " ").trim();
|