@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.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/lib/Engine.ts
|
|
2
9
|
import path4 from "path";
|
|
3
10
|
|
|
@@ -132,15 +139,14 @@ import * as unzipper from "unzipper";
|
|
|
132
139
|
import ora from "ora";
|
|
133
140
|
import chalk from "chalk";
|
|
134
141
|
import { fileURLToPath } from "url";
|
|
135
|
-
import { dirname } from "path";
|
|
142
|
+
import { dirname as dirname2 } from "path";
|
|
136
143
|
var { https } = followRedirects;
|
|
137
144
|
var Downloader = class {
|
|
138
145
|
static mainSpinner = null;
|
|
139
146
|
static currentSpinner = null;
|
|
140
147
|
static VERSION_URLS = {
|
|
141
148
|
query: "https://raw.githubusercontent.com/Dbcube/binaries/main/query-engines.json",
|
|
142
|
-
schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json"
|
|
143
|
-
sqlite: "https://raw.githubusercontent.com/Dbcube/binaries/main/sqlite-engines.json"
|
|
149
|
+
schema: "https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json"
|
|
144
150
|
};
|
|
145
151
|
/**
|
|
146
152
|
* Fetch latest version from GitHub
|
|
@@ -310,7 +316,7 @@ var Downloader = class {
|
|
|
310
316
|
spinner: "dots12"
|
|
311
317
|
}).start();
|
|
312
318
|
const binariesToProcess = [];
|
|
313
|
-
for (const prefix of ["query", "schema"
|
|
319
|
+
for (const prefix of ["query", "schema"]) {
|
|
314
320
|
try {
|
|
315
321
|
const localVersion = this.getLocalVersion(binDir, prefix);
|
|
316
322
|
const remoteVersion = await this.fetchLatestVersion(prefix);
|
|
@@ -475,34 +481,46 @@ var Downloader = class {
|
|
|
475
481
|
}
|
|
476
482
|
static extractBinary(zipPath, outputPath, prefix) {
|
|
477
483
|
return new Promise((resolve5, reject) => {
|
|
478
|
-
|
|
484
|
+
const outDir = path.dirname(outputPath);
|
|
485
|
+
let extracted = 0;
|
|
486
|
+
const pending = [];
|
|
479
487
|
fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", (entry) => {
|
|
480
|
-
if (entry.type
|
|
481
|
-
|
|
482
|
-
|
|
488
|
+
if (entry.type !== "File") {
|
|
489
|
+
entry.autodrain();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const isMain = extracted === 0;
|
|
493
|
+
extracted++;
|
|
494
|
+
const target = isMain ? outputPath : path.join(outDir, path.basename(entry.path));
|
|
495
|
+
pending.push(new Promise((res, rej) => {
|
|
496
|
+
const writeStream = fs.createWriteStream(target);
|
|
483
497
|
entry.pipe(writeStream);
|
|
484
498
|
writeStream.on("finish", () => {
|
|
485
499
|
if (process.platform !== "win32") {
|
|
486
|
-
|
|
500
|
+
try {
|
|
501
|
+
fs.chmodSync(target, 493);
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
487
504
|
}
|
|
488
|
-
|
|
489
|
-
resolve5();
|
|
490
|
-
});
|
|
491
|
-
writeStream.on("error", (err) => {
|
|
492
|
-
this.cleanupFile(zipPath);
|
|
493
|
-
reject(err);
|
|
505
|
+
res();
|
|
494
506
|
});
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
507
|
+
writeStream.on("error", rej);
|
|
508
|
+
}));
|
|
498
509
|
}).on("error", (err) => {
|
|
499
510
|
this.cleanupFile(zipPath);
|
|
500
511
|
reject(err);
|
|
501
512
|
}).on("close", () => {
|
|
502
|
-
|
|
513
|
+
Promise.all(pending).then(() => {
|
|
503
514
|
this.cleanupFile(zipPath);
|
|
504
|
-
|
|
505
|
-
|
|
515
|
+
if (extracted === 0) {
|
|
516
|
+
reject(new Error(`No se encontr\xF3 archivo v\xE1lido en el ZIP para ${prefix}`));
|
|
517
|
+
} else {
|
|
518
|
+
resolve5();
|
|
519
|
+
}
|
|
520
|
+
}).catch((err) => {
|
|
521
|
+
this.cleanupFile(zipPath);
|
|
522
|
+
reject(err);
|
|
523
|
+
});
|
|
506
524
|
});
|
|
507
525
|
});
|
|
508
526
|
}
|
|
@@ -516,7 +534,7 @@ var Downloader = class {
|
|
|
516
534
|
}
|
|
517
535
|
static getDefaultBinDir() {
|
|
518
536
|
const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath(import.meta.url) : "";
|
|
519
|
-
const __dirname = __filename2 ?
|
|
537
|
+
const __dirname = __filename2 ? dirname2(__filename2) : process.cwd();
|
|
520
538
|
const possibleDirs = [
|
|
521
539
|
path.resolve(process.cwd(), ".dbcube", "bin"),
|
|
522
540
|
path.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
|
|
@@ -546,7 +564,7 @@ import * as fs2 from "fs";
|
|
|
546
564
|
import * as path2 from "path";
|
|
547
565
|
import * as os3 from "os";
|
|
548
566
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
549
|
-
import { dirname as
|
|
567
|
+
import { dirname as dirname3 } from "path";
|
|
550
568
|
var Binary = class {
|
|
551
569
|
static isDownloading = false;
|
|
552
570
|
static downloadPromise = null;
|
|
@@ -579,7 +597,7 @@ var Binary = class {
|
|
|
579
597
|
}
|
|
580
598
|
static getBinDir() {
|
|
581
599
|
const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath2(import.meta.url) : "";
|
|
582
|
-
const __dirname = __filename2 ?
|
|
600
|
+
const __dirname = __filename2 ? dirname3(__filename2) : process.cwd();
|
|
583
601
|
const possibleDirs = [
|
|
584
602
|
path2.resolve(process.cwd(), ".dbcube", "bin"),
|
|
585
603
|
path2.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
|
|
@@ -895,7 +913,7 @@ var DaemonClient = class _DaemonClient {
|
|
|
895
913
|
});
|
|
896
914
|
}
|
|
897
915
|
async execute(dml, txId) {
|
|
898
|
-
const payload = { action: "execute", dml
|
|
916
|
+
const payload = { action: "execute", dml };
|
|
899
917
|
if (txId) payload.tx_id = txId;
|
|
900
918
|
return this.send(payload);
|
|
901
919
|
}
|
|
@@ -962,8 +980,8 @@ var Engine = class {
|
|
|
962
980
|
const binaryPath = this.binary["query_engine"];
|
|
963
981
|
if (!binaryPath) return null;
|
|
964
982
|
const client = DaemonClient.get(this.name, binaryPath, this.arguments);
|
|
965
|
-
const
|
|
966
|
-
if (!
|
|
983
|
+
const ok2 = await client.ensure();
|
|
984
|
+
if (!ok2) {
|
|
967
985
|
this.daemonFailed = true;
|
|
968
986
|
return null;
|
|
969
987
|
}
|
|
@@ -1175,14 +1193,265 @@ var Engine = class {
|
|
|
1175
1193
|
};
|
|
1176
1194
|
|
|
1177
1195
|
// src/lib/QueryEngine.ts
|
|
1178
|
-
import
|
|
1196
|
+
import path6 from "path";
|
|
1197
|
+
|
|
1198
|
+
// src/lib/EmbeddedEngine.ts
|
|
1199
|
+
import * as fs4 from "fs";
|
|
1200
|
+
import * as path5 from "path";
|
|
1201
|
+
if (!process.env.UV_THREADPOOL_SIZE) {
|
|
1202
|
+
process.env.UV_THREADPOOL_SIZE = "16";
|
|
1203
|
+
}
|
|
1204
|
+
var backend = null;
|
|
1205
|
+
var loadFailed = false;
|
|
1206
|
+
var handles = /* @__PURE__ */ new Map();
|
|
1207
|
+
var connecting = /* @__PURE__ */ new Map();
|
|
1208
|
+
function platTag() {
|
|
1209
|
+
const arch2 = process.arch === "arm64" ? "arm64" : "x64";
|
|
1210
|
+
if (process.platform === "win32") return { plat: "windows", ext: "dll", arch: arch2 };
|
|
1211
|
+
if (process.platform === "darwin") return { plat: "macos", ext: "dylib", arch: arch2 };
|
|
1212
|
+
return { plat: "linux", ext: "so", arch: arch2 };
|
|
1213
|
+
}
|
|
1214
|
+
function findFile(name) {
|
|
1215
|
+
const candidates = [
|
|
1216
|
+
path5.resolve(process.cwd(), ".dbcube", "bin", name),
|
|
1217
|
+
path5.resolve(process.cwd(), "node_modules", ".dbcube", "bin", name)
|
|
1218
|
+
];
|
|
1219
|
+
for (const c of candidates) {
|
|
1220
|
+
if (fs4.existsSync(c)) return c;
|
|
1221
|
+
}
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
function ok(data) {
|
|
1225
|
+
return { status: 200, message: "OK", data };
|
|
1226
|
+
}
|
|
1227
|
+
function errResp(e) {
|
|
1228
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1229
|
+
return { status: 500, message: msg, data: null };
|
|
1230
|
+
}
|
|
1231
|
+
function loadNapiBackend() {
|
|
1232
|
+
const { plat, arch: arch2 } = platTag();
|
|
1233
|
+
const addonPath = findFile(`query-engine-node-${plat}-${arch2}.node`);
|
|
1234
|
+
if (!addonPath) return null;
|
|
1235
|
+
try {
|
|
1236
|
+
const addon = __require(addonPath);
|
|
1237
|
+
if (typeof addon.connect !== "function") return null;
|
|
1238
|
+
return {
|
|
1239
|
+
async connect(cfgJson) {
|
|
1240
|
+
return Number(await addon.connect(cfgJson));
|
|
1241
|
+
},
|
|
1242
|
+
async execute(handle, dml, txId) {
|
|
1243
|
+
try {
|
|
1244
|
+
const rows = await addon.execute(handle, dml, txId ?? void 0);
|
|
1245
|
+
return ok(rows);
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
return errResp(e);
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
async executeBatch(handle, ops) {
|
|
1251
|
+
try {
|
|
1252
|
+
const results = await addon.executeBatch(handle, ops);
|
|
1253
|
+
return ok(results);
|
|
1254
|
+
} catch (e) {
|
|
1255
|
+
return errResp(e);
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
async raw(handle, query, params, txId) {
|
|
1259
|
+
try {
|
|
1260
|
+
const rows = await addon.raw(handle, query, params, txId ?? void 0);
|
|
1261
|
+
return ok(rows);
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
return errResp(e);
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
async begin(handle) {
|
|
1267
|
+
try {
|
|
1268
|
+
const txId = await addon.begin(handle);
|
|
1269
|
+
return ok({ tx_id: txId });
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
return errResp(e);
|
|
1272
|
+
}
|
|
1273
|
+
},
|
|
1274
|
+
async commit(_handle, txId) {
|
|
1275
|
+
try {
|
|
1276
|
+
await addon.commit(txId);
|
|
1277
|
+
return ok(null);
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
return errResp(e);
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
async rollback(_handle, txId) {
|
|
1283
|
+
try {
|
|
1284
|
+
await addon.rollback(txId);
|
|
1285
|
+
return ok(null);
|
|
1286
|
+
} catch (e) {
|
|
1287
|
+
return errResp(e);
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
async disconnect(handle) {
|
|
1291
|
+
try {
|
|
1292
|
+
addon.disconnect(handle);
|
|
1293
|
+
} catch {
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
} catch {
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
function loadKoffiBackend() {
|
|
1302
|
+
const { plat, ext, arch: arch2 } = platTag();
|
|
1303
|
+
const libPath = findFile(`query-engine-embedded-${plat}-${arch2}.${ext}`);
|
|
1304
|
+
if (!libPath) return null;
|
|
1305
|
+
try {
|
|
1306
|
+
const koffi = __require("koffi");
|
|
1307
|
+
const lib = koffi.load(libPath);
|
|
1308
|
+
const cConnect = lib.func("dbc_connect", "void *", ["str"]);
|
|
1309
|
+
const cExecute = lib.func("dbc_execute", "void *", ["uint64", "str", "str"]);
|
|
1310
|
+
const cExecuteBatch = lib.func("dbc_execute_batch", "void *", ["uint64", "str"]);
|
|
1311
|
+
const cRaw = lib.func("dbc_raw", "void *", ["uint64", "str", "str", "str"]);
|
|
1312
|
+
const cBegin = lib.func("dbc_begin", "void *", ["uint64"]);
|
|
1313
|
+
const cCommit = lib.func("dbc_commit", "void *", ["uint64", "str"]);
|
|
1314
|
+
const cRollback = lib.func("dbc_rollback", "void *", ["uint64", "str"]);
|
|
1315
|
+
const cDisconnect = lib.func("dbc_disconnect", "void *", ["uint64"]);
|
|
1316
|
+
const cFree = lib.func("dbc_free", "void", ["void *"]);
|
|
1317
|
+
const take = (ptr) => {
|
|
1318
|
+
if (!ptr) return { status: 500, message: "embedded engine returned null", data: null };
|
|
1319
|
+
const s = koffi.decode(ptr, "char", -1);
|
|
1320
|
+
cFree(ptr);
|
|
1321
|
+
try {
|
|
1322
|
+
const r = JSON.parse(s);
|
|
1323
|
+
return { status: r.status ?? 500, message: r.message ?? "", data: r.data ?? null };
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
return { status: 500, message: `Invalid embedded response: ${e.message}`, data: null };
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1328
|
+
const call = (fn, ...args) => new Promise((resolve5, reject) => {
|
|
1329
|
+
fn.async(...args, (err, ptr) => {
|
|
1330
|
+
if (err) return reject(err);
|
|
1331
|
+
resolve5(take(ptr));
|
|
1332
|
+
});
|
|
1333
|
+
});
|
|
1334
|
+
return {
|
|
1335
|
+
async connect(cfgJson) {
|
|
1336
|
+
const res = await call(cConnect, cfgJson);
|
|
1337
|
+
if (res.status !== 200 || !res.data?.handle) {
|
|
1338
|
+
throw new Error(String(res.message || "embedded connect failed"));
|
|
1339
|
+
}
|
|
1340
|
+
return Number(res.data.handle);
|
|
1341
|
+
},
|
|
1342
|
+
execute: (handle, dml, txId) => call(cExecute, handle, JSON.stringify(dml), txId),
|
|
1343
|
+
executeBatch: (handle, ops) => call(cExecuteBatch, handle, JSON.stringify(ops)),
|
|
1344
|
+
raw: (handle, query, params, txId) => call(cRaw, handle, query, JSON.stringify(params ?? []), txId),
|
|
1345
|
+
begin: (handle) => call(cBegin, handle),
|
|
1346
|
+
commit: (handle, txId) => call(cCommit, handle, txId),
|
|
1347
|
+
rollback: (handle, txId) => call(cRollback, handle, txId),
|
|
1348
|
+
async disconnect(handle) {
|
|
1349
|
+
try {
|
|
1350
|
+
await call(cDisconnect, handle);
|
|
1351
|
+
} catch {
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
} catch {
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
function loadBackend() {
|
|
1360
|
+
if (backend) return backend;
|
|
1361
|
+
if (loadFailed) return null;
|
|
1362
|
+
if (process.env.DBCUBE_EMBEDDED === "0" || process.env.DBCUBE_EMBEDDED === "false") {
|
|
1363
|
+
loadFailed = true;
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
backend = loadNapiBackend() ?? loadKoffiBackend();
|
|
1367
|
+
if (!backend) loadFailed = true;
|
|
1368
|
+
return backend;
|
|
1369
|
+
}
|
|
1370
|
+
var EmbeddedEngine = class {
|
|
1371
|
+
/** true si algún backend nativo está disponible en esta instalación. */
|
|
1372
|
+
static available() {
|
|
1373
|
+
return loadBackend() !== null;
|
|
1374
|
+
}
|
|
1375
|
+
static async handleFor(connectionId, config) {
|
|
1376
|
+
const existing = handles.get(connectionId);
|
|
1377
|
+
if (existing) return existing;
|
|
1378
|
+
const inFlight = connecting.get(connectionId);
|
|
1379
|
+
if (inFlight) return inFlight;
|
|
1380
|
+
const promise = (async () => {
|
|
1381
|
+
const be = loadBackend();
|
|
1382
|
+
if (!be) throw new Error("embedded engine not available");
|
|
1383
|
+
const motor = config.type === "postgres" ? "postgresql" : config.type;
|
|
1384
|
+
const cfg = {
|
|
1385
|
+
databaseRef: connectionId,
|
|
1386
|
+
motor,
|
|
1387
|
+
database: config.type === "sqlite" ? `${config.config.DATABASE}.db` : config.config.DATABASE,
|
|
1388
|
+
host: config.config.HOST,
|
|
1389
|
+
port: config.config.PORT != null ? Number(config.config.PORT) : void 0,
|
|
1390
|
+
user: config.config.USER,
|
|
1391
|
+
password: config.config.PASSWORD,
|
|
1392
|
+
maxConnections: config.pool?.maxConnections,
|
|
1393
|
+
minConnections: config.pool?.minConnections,
|
|
1394
|
+
acquireTimeoutMs: config.pool?.acquireTimeoutMs,
|
|
1395
|
+
idleTimeoutMs: config.pool?.idleTimeoutMs
|
|
1396
|
+
};
|
|
1397
|
+
const handle = await be.connect(JSON.stringify(cfg));
|
|
1398
|
+
handles.set(connectionId, handle);
|
|
1399
|
+
return handle;
|
|
1400
|
+
})();
|
|
1401
|
+
connecting.set(connectionId, promise);
|
|
1402
|
+
try {
|
|
1403
|
+
return await promise;
|
|
1404
|
+
} finally {
|
|
1405
|
+
connecting.delete(connectionId);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
static async executeDml(connectionId, config, dml, txId) {
|
|
1409
|
+
const be = loadBackend();
|
|
1410
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1411
|
+
return be.execute(handle, dml, txId ?? null);
|
|
1412
|
+
}
|
|
1413
|
+
static async executeBatch(connectionId, config, ops) {
|
|
1414
|
+
const be = loadBackend();
|
|
1415
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1416
|
+
return be.executeBatch(handle, ops);
|
|
1417
|
+
}
|
|
1418
|
+
static async rawQuery(connectionId, config, query, params, txId) {
|
|
1419
|
+
const be = loadBackend();
|
|
1420
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1421
|
+
return be.raw(handle, query, params ?? [], txId ?? null);
|
|
1422
|
+
}
|
|
1423
|
+
static async begin(connectionId, config) {
|
|
1424
|
+
const be = loadBackend();
|
|
1425
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1426
|
+
return be.begin(handle);
|
|
1427
|
+
}
|
|
1428
|
+
static async commit(connectionId, config, txId) {
|
|
1429
|
+
const be = loadBackend();
|
|
1430
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1431
|
+
return be.commit(handle, txId);
|
|
1432
|
+
}
|
|
1433
|
+
static async rollback(connectionId, config, txId) {
|
|
1434
|
+
const be = loadBackend();
|
|
1435
|
+
const handle = await this.handleFor(connectionId, config);
|
|
1436
|
+
return be.rollback(handle, txId);
|
|
1437
|
+
}
|
|
1438
|
+
static async disconnect(connectionId) {
|
|
1439
|
+
const be = loadBackend();
|
|
1440
|
+
const handle = handles.get(connectionId);
|
|
1441
|
+
if (be && handle) {
|
|
1442
|
+
handles.delete(connectionId);
|
|
1443
|
+
await be.disconnect(handle);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
// src/lib/QueryEngine.ts
|
|
1179
1449
|
import { createRequire as createRequire2 } from "module";
|
|
1180
1450
|
import * as net2 from "net";
|
|
1181
1451
|
import { spawn as spawn3 } from "child_process";
|
|
1182
1452
|
var globalTcpServers = /* @__PURE__ */ new Map();
|
|
1183
|
-
var
|
|
1184
|
-
var
|
|
1185
|
-
var connectionProcessing = /* @__PURE__ */ new Map();
|
|
1453
|
+
var socketPools = /* @__PURE__ */ new Map();
|
|
1454
|
+
var CLIENT_POOL_SIZE = 10;
|
|
1186
1455
|
var queryCache = /* @__PURE__ */ new Map();
|
|
1187
1456
|
var MAX_CACHE_SIZE = 500;
|
|
1188
1457
|
var QueryEngine = class {
|
|
@@ -1193,9 +1462,9 @@ var QueryEngine = class {
|
|
|
1193
1462
|
timeout;
|
|
1194
1463
|
connectionId;
|
|
1195
1464
|
tcpPort;
|
|
1196
|
-
constructor(name, timeout = 3e4) {
|
|
1465
|
+
constructor(name, timeout = 3e4, explicitConfig) {
|
|
1197
1466
|
this.name = name;
|
|
1198
|
-
this.config = this.setConfig(name);
|
|
1467
|
+
this.config = explicitConfig ?? this.setConfig(name);
|
|
1199
1468
|
this.arguments = this.setArguments();
|
|
1200
1469
|
this.timeout = this.config?.daemon?.requestTimeoutMs ?? timeout;
|
|
1201
1470
|
this.connectionId = `${name}_query_engine_${this.config.type}_${this.config.config.DATABASE}_${this.config.config.HOST || "localhost"}`;
|
|
@@ -1279,7 +1548,7 @@ var QueryEngine = class {
|
|
|
1279
1548
|
setConfig(name) {
|
|
1280
1549
|
const configInstance = new Config();
|
|
1281
1550
|
try {
|
|
1282
|
-
const configFilePath =
|
|
1551
|
+
const configFilePath = path6.resolve(process.cwd(), "dbcube.config.js");
|
|
1283
1552
|
const requireUrl = typeof __filename !== "undefined" ? __filename : process.cwd();
|
|
1284
1553
|
const require2 = createRequire2(requireUrl);
|
|
1285
1554
|
if (require2.cache && require2.resolve) {
|
|
@@ -1325,38 +1594,79 @@ var QueryEngine = class {
|
|
|
1325
1594
|
}
|
|
1326
1595
|
return command;
|
|
1327
1596
|
}
|
|
1597
|
+
/** El motor embebido (FFI) es el camino por defecto cuando la librería
|
|
1598
|
+
* nativa está disponible; el daemon TCP queda como fallback y como modo
|
|
1599
|
+
* multi-proceso explícito. */
|
|
1600
|
+
useEmbedded() {
|
|
1601
|
+
return EmbeddedEngine.available();
|
|
1602
|
+
}
|
|
1328
1603
|
/**
|
|
1329
|
-
* Executes a DML plan
|
|
1604
|
+
* Executes a DML plan. Embedded engine when available (in-process FFI,
|
|
1605
|
+
* no network hop); persistent TCP daemon otherwise.
|
|
1330
1606
|
* Pass txId to run it inside an active transaction.
|
|
1331
1607
|
*/
|
|
1332
1608
|
async executeDml(dml, txId) {
|
|
1333
|
-
|
|
1609
|
+
if (this.useEmbedded()) {
|
|
1610
|
+
return EmbeddedEngine.executeDml(this.connectionId, this.config, dml, txId);
|
|
1611
|
+
}
|
|
1612
|
+
const command = { action: "execute", dml };
|
|
1334
1613
|
if (txId) command.tx_id = txId;
|
|
1335
1614
|
return this.executeWithTcpServer(command);
|
|
1336
1615
|
}
|
|
1337
1616
|
/**
|
|
1338
|
-
*
|
|
1339
|
-
* over the
|
|
1617
|
+
* Atomic batch: N write plans inside one transaction with a single
|
|
1618
|
+
* engine round-trip. Falls back to begin/ops/commit over the daemon
|
|
1619
|
+
* when the embedded engine is not available.
|
|
1620
|
+
*/
|
|
1621
|
+
async executeBatch(ops) {
|
|
1622
|
+
if (this.useEmbedded()) {
|
|
1623
|
+
return EmbeddedEngine.executeBatch(this.connectionId, this.config, ops);
|
|
1624
|
+
}
|
|
1625
|
+
const txId = await this.beginTransaction();
|
|
1626
|
+
try {
|
|
1627
|
+
const results = [];
|
|
1628
|
+
for (const op of ops) {
|
|
1629
|
+
const res = await this.executeDml(op, txId);
|
|
1630
|
+
if (res.status !== 200) {
|
|
1631
|
+
throw new Error(String(res.message));
|
|
1632
|
+
}
|
|
1633
|
+
results.push(res.data);
|
|
1634
|
+
}
|
|
1635
|
+
await this.commitTransaction(txId);
|
|
1636
|
+
return { status: 200, message: "Batch committed", data: results };
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
try {
|
|
1639
|
+
await this.rollbackTransaction(txId);
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
return { status: 500, message: error?.message ?? String(error), data: null };
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Executes raw SQL (or a MongoDB command document) with bound parameters.
|
|
1340
1647
|
*/
|
|
1341
1648
|
async rawQuery(query, params = [], txId) {
|
|
1649
|
+
if (this.useEmbedded()) {
|
|
1650
|
+
return EmbeddedEngine.rawQuery(this.connectionId, this.config, query, params, txId);
|
|
1651
|
+
}
|
|
1342
1652
|
const command = { action: "raw", query, params };
|
|
1343
1653
|
if (txId) command.tx_id = txId;
|
|
1344
1654
|
return this.executeWithTcpServer(command);
|
|
1345
1655
|
}
|
|
1346
|
-
/** Starts a
|
|
1656
|
+
/** Starts a transaction and returns its id. */
|
|
1347
1657
|
async beginTransaction() {
|
|
1348
|
-
const res = await this.executeWithTcpServer({ action: "begin" });
|
|
1658
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.begin(this.connectionId, this.config) : await this.executeWithTcpServer({ action: "begin" });
|
|
1349
1659
|
if (res.status !== 200 || !res.data?.tx_id) {
|
|
1350
1660
|
throw new Error(String(res.message || "Failed to begin transaction (update the query-engine binary: npx dbcube update)"));
|
|
1351
1661
|
}
|
|
1352
1662
|
return res.data.tx_id;
|
|
1353
1663
|
}
|
|
1354
1664
|
async commitTransaction(txId) {
|
|
1355
|
-
const res = await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1665
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.commit(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "commit", tx_id: txId });
|
|
1356
1666
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to commit transaction"));
|
|
1357
1667
|
}
|
|
1358
1668
|
async rollbackTransaction(txId) {
|
|
1359
|
-
const res = await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1669
|
+
const res = this.useEmbedded() ? await EmbeddedEngine.rollback(this.connectionId, this.config, txId) : await this.executeWithTcpServer({ action: "rollback", tx_id: txId });
|
|
1360
1670
|
if (res.status !== 200) throw new Error(String(res.message || "Failed to rollback transaction"));
|
|
1361
1671
|
}
|
|
1362
1672
|
async executeWithTcpServer(command) {
|
|
@@ -1375,7 +1685,63 @@ var QueryEngine = class {
|
|
|
1375
1685
|
await this.startTcpServer();
|
|
1376
1686
|
await this.waitForServerReady();
|
|
1377
1687
|
}
|
|
1378
|
-
|
|
1688
|
+
const port = globalTcpServers.get(this.connectionId).port;
|
|
1689
|
+
const socket = await this.acquireSocket(port);
|
|
1690
|
+
try {
|
|
1691
|
+
const result = await this.executeOnConnection(socket, command);
|
|
1692
|
+
this.releaseSocket(socket, false);
|
|
1693
|
+
return result;
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
this.releaseSocket(socket, true);
|
|
1696
|
+
throw error;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
poolFor() {
|
|
1700
|
+
let pool = socketPools.get(this.connectionId);
|
|
1701
|
+
if (!pool) {
|
|
1702
|
+
pool = { free: [], total: 0, waiters: [] };
|
|
1703
|
+
socketPools.set(this.connectionId, pool);
|
|
1704
|
+
}
|
|
1705
|
+
return pool;
|
|
1706
|
+
}
|
|
1707
|
+
async acquireSocket(port) {
|
|
1708
|
+
const pool = this.poolFor();
|
|
1709
|
+
while (pool.free.length > 0) {
|
|
1710
|
+
const s = pool.free.pop();
|
|
1711
|
+
if (!s.destroyed) return s;
|
|
1712
|
+
pool.total--;
|
|
1713
|
+
}
|
|
1714
|
+
if (pool.total < CLIENT_POOL_SIZE) {
|
|
1715
|
+
pool.total++;
|
|
1716
|
+
try {
|
|
1717
|
+
return await this.createNewConnection(port);
|
|
1718
|
+
} catch (e) {
|
|
1719
|
+
pool.total--;
|
|
1720
|
+
throw e;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
return new Promise((resolve5) => pool.waiters.push(resolve5));
|
|
1724
|
+
}
|
|
1725
|
+
releaseSocket(socket, broken) {
|
|
1726
|
+
const pool = socketPools.get(this.connectionId);
|
|
1727
|
+
if (!pool) {
|
|
1728
|
+
socket.destroy();
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
if (broken || socket.destroyed) {
|
|
1732
|
+
pool.total--;
|
|
1733
|
+
try {
|
|
1734
|
+
socket.destroy();
|
|
1735
|
+
} catch {
|
|
1736
|
+
}
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
const waiter = pool.waiters.shift();
|
|
1740
|
+
if (waiter) {
|
|
1741
|
+
waiter(socket);
|
|
1742
|
+
} else {
|
|
1743
|
+
pool.free.push(socket);
|
|
1744
|
+
}
|
|
1379
1745
|
}
|
|
1380
1746
|
async waitForServerReady() {
|
|
1381
1747
|
const maxRetries = 10;
|
|
@@ -1469,58 +1835,6 @@ var QueryEngine = class {
|
|
|
1469
1835
|
});
|
|
1470
1836
|
});
|
|
1471
1837
|
}
|
|
1472
|
-
async sendTcpRequestFast(command) {
|
|
1473
|
-
return new Promise((resolve5, reject) => {
|
|
1474
|
-
let queue = connectionQueues.get(this.connectionId);
|
|
1475
|
-
if (!queue) {
|
|
1476
|
-
queue = [];
|
|
1477
|
-
connectionQueues.set(this.connectionId, queue);
|
|
1478
|
-
}
|
|
1479
|
-
queue.push({ command, resolve: resolve5, reject });
|
|
1480
|
-
this.processQueue();
|
|
1481
|
-
});
|
|
1482
|
-
}
|
|
1483
|
-
async processQueue() {
|
|
1484
|
-
if (connectionProcessing.get(this.connectionId)) {
|
|
1485
|
-
return;
|
|
1486
|
-
}
|
|
1487
|
-
const queue = connectionQueues.get(this.connectionId);
|
|
1488
|
-
if (!queue || queue.length === 0) {
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
connectionProcessing.set(this.connectionId, true);
|
|
1492
|
-
try {
|
|
1493
|
-
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1494
|
-
if (!serverInfo) {
|
|
1495
|
-
throw new Error("Server not initialized");
|
|
1496
|
-
}
|
|
1497
|
-
while (queue.length > 0) {
|
|
1498
|
-
const request = queue.shift();
|
|
1499
|
-
if (!request) break;
|
|
1500
|
-
try {
|
|
1501
|
-
let connection = globalTcpConnections.get(this.connectionId);
|
|
1502
|
-
if (!connection || connection.destroyed || connection.readyState !== "open") {
|
|
1503
|
-
connection = await this.createNewConnection(serverInfo.port);
|
|
1504
|
-
globalTcpConnections.set(this.connectionId, connection);
|
|
1505
|
-
}
|
|
1506
|
-
const result = await this.executeOnConnection(connection, request.command);
|
|
1507
|
-
request.resolve(result);
|
|
1508
|
-
} catch (error) {
|
|
1509
|
-
const staleConnection = globalTcpConnections.get(this.connectionId);
|
|
1510
|
-
if (staleConnection) {
|
|
1511
|
-
try {
|
|
1512
|
-
staleConnection.destroy();
|
|
1513
|
-
} catch {
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
globalTcpConnections.delete(this.connectionId);
|
|
1517
|
-
request.reject(error);
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
} finally {
|
|
1521
|
-
connectionProcessing.set(this.connectionId, false);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
1838
|
async createNewConnection(port) {
|
|
1525
1839
|
return new Promise((resolve5, reject) => {
|
|
1526
1840
|
const client = new net2.Socket();
|
|
@@ -1668,11 +1982,16 @@ var QueryEngine = class {
|
|
|
1668
1982
|
});
|
|
1669
1983
|
}
|
|
1670
1984
|
async disconnect() {
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1985
|
+
await EmbeddedEngine.disconnect(this.connectionId);
|
|
1986
|
+
const pool = socketPools.get(this.connectionId);
|
|
1987
|
+
if (pool) {
|
|
1988
|
+
for (const s of pool.free) {
|
|
1989
|
+
try {
|
|
1990
|
+
s.destroy();
|
|
1991
|
+
} catch {
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
socketPools.delete(this.connectionId);
|
|
1676
1995
|
}
|
|
1677
1996
|
const serverInfo = globalTcpServers.get(this.connectionId);
|
|
1678
1997
|
if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
|
|
@@ -1692,346 +2011,70 @@ var QueryEngine = class {
|
|
|
1692
2011
|
}
|
|
1693
2012
|
};
|
|
1694
2013
|
|
|
1695
|
-
// src/lib/SqliteExecutor.ts
|
|
1696
|
-
import { exec } from "child_process";
|
|
1697
|
-
import * as path6 from "path";
|
|
1698
|
-
import * as fs4 from "fs";
|
|
1699
|
-
import { promisify } from "util";
|
|
1700
|
-
import { createRequire as createRequire3 } from "module";
|
|
1701
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1702
|
-
import { dirname as dirname3 } from "path";
|
|
1703
|
-
var execAsync = promisify(exec);
|
|
1704
|
-
var SqliteExecutor = class {
|
|
1705
|
-
binaryPath;
|
|
1706
|
-
dbPath;
|
|
1707
|
-
constructor(dbPath) {
|
|
1708
|
-
this.dbPath = dbPath;
|
|
1709
|
-
this.binaryPath = this.getBinaryPath();
|
|
1710
|
-
}
|
|
1711
|
-
findVersionedBinary(binDir, platform2) {
|
|
1712
|
-
try {
|
|
1713
|
-
const files = fs4.readdirSync(binDir);
|
|
1714
|
-
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1715
|
-
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1716
|
-
const pattern = new RegExp(`^sqlite-engine-v\\d+\\.\\d+\\.\\d+-${platformName}-x64${extension.replace(".", "\\.")}$`);
|
|
1717
|
-
const matchingFile = files.find((f) => pattern.test(f));
|
|
1718
|
-
if (matchingFile) {
|
|
1719
|
-
return path6.join(binDir, matchingFile);
|
|
1720
|
-
}
|
|
1721
|
-
} catch (error) {
|
|
1722
|
-
}
|
|
1723
|
-
return null;
|
|
1724
|
-
}
|
|
1725
|
-
getBinaryPath() {
|
|
1726
|
-
const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath3(import.meta.url) : "";
|
|
1727
|
-
const __dirname = __filename2 ? dirname3(__filename2) : process.cwd();
|
|
1728
|
-
const possibleDirs = [
|
|
1729
|
-
path6.resolve(process.cwd(), ".dbcube", "bin"),
|
|
1730
|
-
path6.resolve(process.cwd(), "node_modules", ".dbcube", "bin"),
|
|
1731
|
-
path6.resolve(__dirname, "..", "bin")
|
|
1732
|
-
];
|
|
1733
|
-
const platform2 = process.platform;
|
|
1734
|
-
const extension = platform2 === "win32" ? ".exe" : "";
|
|
1735
|
-
const platformName = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "macos" : "linux";
|
|
1736
|
-
for (const dir of possibleDirs) {
|
|
1737
|
-
const versionedPath = this.findVersionedBinary(dir, platform2);
|
|
1738
|
-
if (versionedPath && fs4.existsSync(versionedPath)) {
|
|
1739
|
-
return versionedPath;
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
const binaryName = `sqlite-engine-${platformName}-x64${extension}`;
|
|
1743
|
-
for (const dir of possibleDirs) {
|
|
1744
|
-
const fullPath = path6.join(dir, binaryName);
|
|
1745
|
-
if (fs4.existsSync(fullPath)) {
|
|
1746
|
-
return fullPath;
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
const fallbackName = `sqlite-engine${extension}`;
|
|
1750
|
-
for (const dir of possibleDirs) {
|
|
1751
|
-
const fullPath = path6.join(dir, fallbackName);
|
|
1752
|
-
if (fs4.existsSync(fullPath)) {
|
|
1753
|
-
return fullPath;
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
return path6.join(possibleDirs[0], binaryName);
|
|
1757
|
-
}
|
|
1758
|
-
async executeBinary(args) {
|
|
1759
|
-
const escapedArgs = args.map((arg) => {
|
|
1760
|
-
if (arg.includes(" ") || arg.includes('"') || arg.includes("(") || arg.includes(")")) {
|
|
1761
|
-
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
1762
|
-
}
|
|
1763
|
-
return arg;
|
|
1764
|
-
});
|
|
1765
|
-
const command = `"${this.binaryPath}" ${escapedArgs.join(" ")}`;
|
|
1766
|
-
try {
|
|
1767
|
-
const { stdout, stderr } = await execAsync(command, {
|
|
1768
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
1769
|
-
// 10MB buffer
|
|
1770
|
-
timeout: 3e4
|
|
1771
|
-
// 30s timeout
|
|
1772
|
-
});
|
|
1773
|
-
if (stderr && stderr.trim()) {
|
|
1774
|
-
console.warn("SQLite Engine Warning:", stderr);
|
|
1775
|
-
}
|
|
1776
|
-
const result = JSON.parse(stdout.trim());
|
|
1777
|
-
return result;
|
|
1778
|
-
} catch (error) {
|
|
1779
|
-
if (error.stdout) {
|
|
1780
|
-
try {
|
|
1781
|
-
const result = JSON.parse(error.stdout.trim());
|
|
1782
|
-
return result;
|
|
1783
|
-
} catch (parseError) {
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
throw new Error(`SQLite execution failed: ${error.message}`);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
async connect() {
|
|
1790
|
-
try {
|
|
1791
|
-
const result = await this.executeBinary([
|
|
1792
|
-
"--action",
|
|
1793
|
-
"connect",
|
|
1794
|
-
"--database",
|
|
1795
|
-
this.dbPath
|
|
1796
|
-
]);
|
|
1797
|
-
return result.status === "success";
|
|
1798
|
-
} catch (error) {
|
|
1799
|
-
return false;
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
async exists() {
|
|
1803
|
-
try {
|
|
1804
|
-
const result = await this.executeBinary([
|
|
1805
|
-
"--action",
|
|
1806
|
-
"exists",
|
|
1807
|
-
"--database",
|
|
1808
|
-
this.dbPath
|
|
1809
|
-
]);
|
|
1810
|
-
return result.status === "success" && result.data === true;
|
|
1811
|
-
} catch (error) {
|
|
1812
|
-
return false;
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
async query(sql, params) {
|
|
1816
|
-
const args = [
|
|
1817
|
-
"--action",
|
|
1818
|
-
"query",
|
|
1819
|
-
"--database",
|
|
1820
|
-
this.dbPath,
|
|
1821
|
-
"--query",
|
|
1822
|
-
sql
|
|
1823
|
-
];
|
|
1824
|
-
if (params && params.length > 0) {
|
|
1825
|
-
args.push("--params", JSON.stringify(params));
|
|
1826
|
-
}
|
|
1827
|
-
return this.executeBinary(args);
|
|
1828
|
-
}
|
|
1829
|
-
// Método para múltiples queries (como lo hace better-sqlite3)
|
|
1830
|
-
async queryMultiple(sql) {
|
|
1831
|
-
return this.query(sql);
|
|
1832
|
-
}
|
|
1833
|
-
// Método prepare que simula prepared statements
|
|
1834
|
-
prepare(sql) {
|
|
1835
|
-
return {
|
|
1836
|
-
all: async (...params) => {
|
|
1837
|
-
const result = await this.query(sql, params);
|
|
1838
|
-
if (result.status === "error") {
|
|
1839
|
-
throw new Error(result.message);
|
|
1840
|
-
}
|
|
1841
|
-
return Array.isArray(result.data) ? result.data : [];
|
|
1842
|
-
},
|
|
1843
|
-
run: async (...params) => {
|
|
1844
|
-
const result = await this.query(sql, params);
|
|
1845
|
-
if (result.status === "error") {
|
|
1846
|
-
throw new Error(result.message);
|
|
1847
|
-
}
|
|
1848
|
-
if (typeof result.data === "object" && result.data.hasOwnProperty("changes") && result.data.hasOwnProperty("lastID")) {
|
|
1849
|
-
return result.data;
|
|
1850
|
-
}
|
|
1851
|
-
return { changes: 0, lastID: 0 };
|
|
1852
|
-
}
|
|
1853
|
-
};
|
|
1854
|
-
}
|
|
1855
|
-
// Para compatibilidad con better-sqlite3 API sincrona usando deasync si es necesario
|
|
1856
|
-
prepareSync(sql) {
|
|
1857
|
-
const __filename2 = typeof import.meta !== "undefined" && import.meta.url ? fileURLToPath3(import.meta.url) : "";
|
|
1858
|
-
const requireUrl = __filename2 || import.meta.url;
|
|
1859
|
-
const require2 = createRequire3(requireUrl);
|
|
1860
|
-
const deasync = require2("deasync");
|
|
1861
|
-
return {
|
|
1862
|
-
all: (...params) => {
|
|
1863
|
-
let result;
|
|
1864
|
-
let done = false;
|
|
1865
|
-
let error;
|
|
1866
|
-
this.query(sql, params).then((res) => {
|
|
1867
|
-
if (res.status === "error") {
|
|
1868
|
-
error = new Error(res.message);
|
|
1869
|
-
} else {
|
|
1870
|
-
result = Array.isArray(res.data) ? res.data : [];
|
|
1871
|
-
}
|
|
1872
|
-
done = true;
|
|
1873
|
-
}).catch((err) => {
|
|
1874
|
-
error = err;
|
|
1875
|
-
done = true;
|
|
1876
|
-
});
|
|
1877
|
-
deasync.loopWhile(() => !done);
|
|
1878
|
-
if (error) throw error;
|
|
1879
|
-
return result;
|
|
1880
|
-
},
|
|
1881
|
-
run: (...params) => {
|
|
1882
|
-
let result;
|
|
1883
|
-
let done = false;
|
|
1884
|
-
let error;
|
|
1885
|
-
this.query(sql, params).then((res) => {
|
|
1886
|
-
if (res.status === "error") {
|
|
1887
|
-
error = new Error(res.message);
|
|
1888
|
-
} else if (typeof res.data === "object" && res.data.hasOwnProperty("changes") && res.data.hasOwnProperty("lastID")) {
|
|
1889
|
-
result = res.data;
|
|
1890
|
-
} else {
|
|
1891
|
-
result = { changes: 0, lastID: 0 };
|
|
1892
|
-
}
|
|
1893
|
-
done = true;
|
|
1894
|
-
}).catch((err) => {
|
|
1895
|
-
error = err;
|
|
1896
|
-
done = true;
|
|
1897
|
-
});
|
|
1898
|
-
deasync.loopWhile(() => !done);
|
|
1899
|
-
if (error) throw error;
|
|
1900
|
-
return result;
|
|
1901
|
-
}
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
};
|
|
1905
|
-
|
|
1906
2014
|
// src/lib/DbConfig.ts
|
|
1907
2015
|
import * as path7 from "path";
|
|
1908
2016
|
import fs5 from "fs";
|
|
1909
2017
|
var rootPath = path7.resolve(process.cwd(), ".dbcube");
|
|
1910
2018
|
var SQLite = class {
|
|
1911
|
-
executor = null;
|
|
1912
2019
|
database;
|
|
2020
|
+
engine = null;
|
|
1913
2021
|
constructor(config) {
|
|
1914
|
-
this.database = config.DATABASE;
|
|
2022
|
+
this.database = config.DATABASE || "config";
|
|
2023
|
+
}
|
|
2024
|
+
/** Ruta del archivo SQLite interno (.dbcube/<database>.db). */
|
|
2025
|
+
dbFilePath() {
|
|
2026
|
+
return path7.join(rootPath, this.database + ".db");
|
|
2027
|
+
}
|
|
2028
|
+
/** QueryEngine sqlite apuntando al archivo interno, con config explícito
|
|
2029
|
+
* (no vive en dbcube.config.js). La ruta relativa `.dbcube/<db>` deja que
|
|
2030
|
+
* QueryEngine le añada `.db` → `.dbcube/<db>.db`, relativo al CWD. */
|
|
2031
|
+
getEngine() {
|
|
2032
|
+
if (!this.engine) {
|
|
2033
|
+
const relativeDb = path7.join(".dbcube", this.database).replace(/\\/g, "/");
|
|
2034
|
+
this.engine = new QueryEngine(`dbcube-internal-${this.database}`, 3e4, {
|
|
2035
|
+
type: "sqlite",
|
|
2036
|
+
config: { DATABASE: relativeDb }
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return this.engine;
|
|
1915
2040
|
}
|
|
1916
2041
|
async ifExist() {
|
|
1917
|
-
if (
|
|
1918
|
-
|
|
1919
|
-
const configPath = path7.join(rootPath, dbPath + ".db");
|
|
1920
|
-
if (!fs5.existsSync(rootPath)) {
|
|
1921
|
-
fs5.mkdirSync(rootPath, { recursive: true });
|
|
1922
|
-
}
|
|
1923
|
-
if (fs5.existsSync(configPath)) {
|
|
1924
|
-
return true;
|
|
1925
|
-
}
|
|
1926
|
-
if (!this.executor) {
|
|
1927
|
-
this.executor = new SqliteExecutor(configPath);
|
|
1928
|
-
}
|
|
1929
|
-
return await this.executor.exists();
|
|
2042
|
+
if (!fs5.existsSync(rootPath)) {
|
|
2043
|
+
fs5.mkdirSync(rootPath, { recursive: true });
|
|
1930
2044
|
}
|
|
1931
|
-
return
|
|
2045
|
+
return fs5.existsSync(this.dbFilePath());
|
|
1932
2046
|
}
|
|
1933
2047
|
async connect() {
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
const configPath = path7.join(rootPath, dbPath + ".db");
|
|
1939
|
-
if (!fs5.existsSync(rootPath)) {
|
|
1940
|
-
fs5.mkdirSync(rootPath, { recursive: true });
|
|
1941
|
-
}
|
|
1942
|
-
this.executor = new SqliteExecutor(configPath);
|
|
1943
|
-
const connected = await this.executor.connect();
|
|
1944
|
-
if (!connected) {
|
|
1945
|
-
throw new Error("Failed to connect to SQLite database");
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
resolve5(this.executor);
|
|
1949
|
-
} catch (error) {
|
|
1950
|
-
reject(error);
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
2048
|
+
if (!fs5.existsSync(rootPath)) {
|
|
2049
|
+
fs5.mkdirSync(rootPath, { recursive: true });
|
|
2050
|
+
}
|
|
2051
|
+
return this.getEngine();
|
|
1953
2052
|
}
|
|
1954
2053
|
async disconnect() {
|
|
1955
|
-
return new Promise((resolve5) => {
|
|
1956
|
-
if (this.executor) {
|
|
1957
|
-
this.executor = null;
|
|
1958
|
-
}
|
|
1959
|
-
resolve5();
|
|
1960
|
-
});
|
|
1961
2054
|
}
|
|
1962
2055
|
async query(sqlQuery) {
|
|
1963
|
-
return
|
|
1964
|
-
try {
|
|
1965
|
-
if (typeof sqlQuery !== "string") {
|
|
1966
|
-
throw new Error("The SQL query must be a string.");
|
|
1967
|
-
}
|
|
1968
|
-
if (!this.executor) {
|
|
1969
|
-
await this.connect();
|
|
1970
|
-
}
|
|
1971
|
-
if (!this.executor) {
|
|
1972
|
-
throw new Error("Database connection is not available.");
|
|
1973
|
-
}
|
|
1974
|
-
const result = await this.executor.queryMultiple(sqlQuery);
|
|
1975
|
-
if (result.status === "error") {
|
|
1976
|
-
resolve5({
|
|
1977
|
-
status: "error",
|
|
1978
|
-
message: result.message,
|
|
1979
|
-
data: null
|
|
1980
|
-
});
|
|
1981
|
-
} else {
|
|
1982
|
-
resolve5({
|
|
1983
|
-
status: "success",
|
|
1984
|
-
message: "Query executed successfully",
|
|
1985
|
-
data: result.data
|
|
1986
|
-
});
|
|
1987
|
-
}
|
|
1988
|
-
} catch (error) {
|
|
1989
|
-
resolve5({
|
|
1990
|
-
status: "error",
|
|
1991
|
-
message: error.message || "An error occurred while executing the query.",
|
|
1992
|
-
data: null
|
|
1993
|
-
});
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
2056
|
+
return this.queryWithParameters(sqlQuery, []);
|
|
1996
2057
|
}
|
|
1997
2058
|
async queryWithParameters(sqlQuery, params = []) {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
throw new Error("The SQL query must be a string.");
|
|
2002
|
-
}
|
|
2003
|
-
if (!Array.isArray(params)) {
|
|
2004
|
-
throw new Error("Parameters must be an array.");
|
|
2005
|
-
}
|
|
2006
|
-
if (!this.executor) {
|
|
2007
|
-
await this.connect();
|
|
2008
|
-
}
|
|
2009
|
-
if (!this.executor) {
|
|
2010
|
-
throw new Error("Database connection is not available.");
|
|
2011
|
-
}
|
|
2012
|
-
const result = await this.executor.query(sqlQuery, params);
|
|
2013
|
-
if (result.status === "error") {
|
|
2014
|
-
resolve5({
|
|
2015
|
-
status: "error",
|
|
2016
|
-
message: result.message,
|
|
2017
|
-
data: null
|
|
2018
|
-
});
|
|
2019
|
-
} else {
|
|
2020
|
-
resolve5({
|
|
2021
|
-
status: "success",
|
|
2022
|
-
message: "Query executed successfully",
|
|
2023
|
-
data: result.data
|
|
2024
|
-
});
|
|
2025
|
-
}
|
|
2026
|
-
} catch (error) {
|
|
2027
|
-
console.log(error);
|
|
2028
|
-
resolve5({
|
|
2029
|
-
status: "error",
|
|
2030
|
-
message: error.message || "An error occurred while executing the query.",
|
|
2031
|
-
data: null
|
|
2032
|
-
});
|
|
2059
|
+
try {
|
|
2060
|
+
if (typeof sqlQuery !== "string") {
|
|
2061
|
+
throw new Error("The SQL query must be a string.");
|
|
2033
2062
|
}
|
|
2034
|
-
|
|
2063
|
+
if (!Array.isArray(params)) {
|
|
2064
|
+
throw new Error("Parameters must be an array.");
|
|
2065
|
+
}
|
|
2066
|
+
const response = await this.getEngine().rawQuery(sqlQuery, params);
|
|
2067
|
+
if (response.status !== 200) {
|
|
2068
|
+
return { status: "error", message: response.message || "Query failed", data: null };
|
|
2069
|
+
}
|
|
2070
|
+
return { status: "success", message: "Query executed successfully", data: response.data };
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
return {
|
|
2073
|
+
status: "error",
|
|
2074
|
+
message: error.message || "An error occurred while executing the query.",
|
|
2075
|
+
data: null
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2035
2078
|
}
|
|
2036
2079
|
convertToParameterizedQuery(sql) {
|
|
2037
2080
|
const normalizedSql = sql.replace(/\s+/g, " ").trim();
|