spindb 0.46.5 → 0.47.1
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/cli/commands/info.js +12 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/link.js +6 -0
- package/dist/cli/commands/link.js.map +1 -1
- package/dist/cli/commands/list.js +4 -1
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/menu/container-handlers.js +25 -7
- package/dist/cli/commands/menu/container-handlers.js.map +1 -1
- package/dist/config/backup-formats.js +11 -11
- package/dist/config/backup-formats.js.map +1 -1
- package/dist/config/version.js +1 -1
- package/dist/core/credential-manager.js +34 -11
- package/dist/core/credential-manager.js.map +1 -1
- package/dist/core/query-parser.js +55 -1
- package/dist/core/query-parser.js.map +1 -1
- package/dist/core/remote-container.js +11 -0
- package/dist/core/remote-container.js.map +1 -1
- package/dist/engines/clickhouse/backup.js +42 -10
- package/dist/engines/clickhouse/backup.js.map +1 -1
- package/dist/engines/clickhouse/restore.js +41 -10
- package/dist/engines/clickhouse/restore.js.map +1 -1
- package/dist/engines/cockroachdb/backup.js +18 -22
- package/dist/engines/cockroachdb/backup.js.map +1 -1
- package/dist/engines/cockroachdb/cli-utils.js +66 -0
- package/dist/engines/cockroachdb/cli-utils.js.map +1 -1
- package/dist/engines/cockroachdb/index.js +199 -116
- package/dist/engines/cockroachdb/index.js.map +1 -1
- package/dist/engines/cockroachdb/restore.js +19 -26
- package/dist/engines/cockroachdb/restore.js.map +1 -1
- package/dist/engines/couchdb/backup.js +13 -4
- package/dist/engines/couchdb/backup.js.map +1 -1
- package/dist/engines/couchdb/index.js +93 -25
- package/dist/engines/couchdb/index.js.map +1 -1
- package/dist/engines/couchdb/restore.js +15 -4
- package/dist/engines/couchdb/restore.js.map +1 -1
- package/dist/engines/ferretdb/backup.js +88 -91
- package/dist/engines/ferretdb/backup.js.map +1 -1
- package/dist/engines/ferretdb/index.js +182 -227
- package/dist/engines/ferretdb/index.js.map +1 -1
- package/dist/engines/ferretdb/restore.js +223 -20
- package/dist/engines/ferretdb/restore.js.map +1 -1
- package/dist/engines/influxdb/api-client.js +1 -1
- package/dist/engines/influxdb/api-client.js.map +1 -1
- package/dist/engines/influxdb/backup.js +25 -5
- package/dist/engines/influxdb/backup.js.map +1 -1
- package/dist/engines/influxdb/index.js +165 -43
- package/dist/engines/influxdb/index.js.map +1 -1
- package/dist/engines/influxdb/restore.js +22 -2
- package/dist/engines/influxdb/restore.js.map +1 -1
- package/dist/engines/mariadb/backup.js +24 -15
- package/dist/engines/mariadb/backup.js.map +1 -1
- package/dist/engines/mariadb/env.js +11 -0
- package/dist/engines/mariadb/env.js.map +1 -0
- package/dist/engines/mariadb/index.js +192 -113
- package/dist/engines/mariadb/index.js.map +1 -1
- package/dist/engines/mariadb/restore.js +28 -5
- package/dist/engines/mariadb/restore.js.map +1 -1
- package/dist/engines/meilisearch/backup.js +8 -4
- package/dist/engines/meilisearch/backup.js.map +1 -1
- package/dist/engines/meilisearch/index.js +55 -58
- package/dist/engines/meilisearch/index.js.map +1 -1
- package/dist/engines/mongo-uri.js +11 -0
- package/dist/engines/mongo-uri.js.map +1 -0
- package/dist/engines/mongodb/backup.js +62 -13
- package/dist/engines/mongodb/backup.js.map +1 -1
- package/dist/engines/mongodb/index.js +170 -108
- package/dist/engines/mongodb/index.js.map +1 -1
- package/dist/engines/mongodb/restore.js +21 -1
- package/dist/engines/mongodb/restore.js.map +1 -1
- package/dist/engines/mysql/backup.js +24 -7
- package/dist/engines/mysql/backup.js.map +1 -1
- package/dist/engines/mysql/index.js +154 -89
- package/dist/engines/mysql/index.js.map +1 -1
- package/dist/engines/mysql/restore.js +14 -4
- package/dist/engines/mysql/restore.js.map +1 -1
- package/dist/engines/postgresql/backup.js +9 -2
- package/dist/engines/postgresql/backup.js.map +1 -1
- package/dist/engines/postgresql/index.js +45 -17
- package/dist/engines/postgresql/index.js.map +1 -1
- package/dist/engines/postgresql/restore.js +7 -3
- package/dist/engines/postgresql/restore.js.map +1 -1
- package/dist/engines/qdrant/backup.js +5 -1
- package/dist/engines/qdrant/backup.js.map +1 -1
- package/dist/engines/qdrant/index.js +31 -2
- package/dist/engines/qdrant/index.js.map +1 -1
- package/dist/engines/qdrant/restore.js +5 -3
- package/dist/engines/qdrant/restore.js.map +1 -1
- package/dist/engines/questdb/auth.js +26 -0
- package/dist/engines/questdb/auth.js.map +1 -0
- package/dist/engines/questdb/backup.js +10 -8
- package/dist/engines/questdb/backup.js.map +1 -1
- package/dist/engines/questdb/index.js +16 -8
- package/dist/engines/questdb/index.js.map +1 -1
- package/dist/engines/questdb/restore.js +7 -5
- package/dist/engines/questdb/restore.js.map +1 -1
- package/dist/engines/redis/backup.js +45 -16
- package/dist/engines/redis/backup.js.map +1 -1
- package/dist/engines/redis/cli-common.js +62 -0
- package/dist/engines/redis/cli-common.js.map +1 -0
- package/dist/engines/redis/index.js +31 -8
- package/dist/engines/redis/index.js.map +1 -1
- package/dist/engines/redis/restore.js +59 -9
- package/dist/engines/redis/restore.js.map +1 -1
- package/dist/engines/surrealdb/auth.js +98 -0
- package/dist/engines/surrealdb/auth.js.map +1 -0
- package/dist/engines/surrealdb/backup.js +72 -9
- package/dist/engines/surrealdb/backup.js.map +1 -1
- package/dist/engines/surrealdb/index.js +84 -144
- package/dist/engines/surrealdb/index.js.map +1 -1
- package/dist/engines/surrealdb/restore.js +32 -31
- package/dist/engines/surrealdb/restore.js.map +1 -1
- package/dist/engines/typedb/backup.js +9 -1
- package/dist/engines/typedb/backup.js.map +1 -1
- package/dist/engines/typedb/cli-utils.js +3 -3
- package/dist/engines/typedb/cli-utils.js.map +1 -1
- package/dist/engines/typedb/index.js +9 -2
- package/dist/engines/typedb/index.js.map +1 -1
- package/dist/engines/typedb/restore.js +19 -7
- package/dist/engines/typedb/restore.js.map +1 -1
- package/dist/engines/valkey/backup.js +37 -13
- package/dist/engines/valkey/backup.js.map +1 -1
- package/dist/engines/valkey/index.js +207 -58
- package/dist/engines/valkey/index.js.map +1 -1
- package/dist/engines/valkey/restore.js +21 -2
- package/dist/engines/valkey/restore.js.map +1 -1
- package/dist/engines/weaviate/backup.js +7 -2
- package/dist/engines/weaviate/backup.js.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* Manages MariaDB database containers using pre-built binaries from hostdb
|
|
4
4
|
*/
|
|
5
5
|
import { spawn, exec } from 'child_process';
|
|
6
|
-
import { promisify } from 'util';
|
|
7
6
|
import { existsSync, createReadStream } from 'fs';
|
|
8
7
|
import { mkdir, writeFile, readFile, unlink, rm } from 'fs/promises';
|
|
9
8
|
import { join } from 'path';
|
|
10
9
|
import { BaseEngine } from '../base-engine.js';
|
|
11
10
|
import { paths } from '../../config/paths.js';
|
|
12
11
|
import { getEngineDefaults } from '../../config/defaults.js';
|
|
13
|
-
import { platformService, isWindows,
|
|
12
|
+
import { platformService, isWindows, } from '../../core/platform-service.js';
|
|
14
13
|
import { configManager } from '../../core/config-manager.js';
|
|
14
|
+
import { getDefaultUsername, loadCredentials, } from '../../core/credential-manager.js';
|
|
15
15
|
import { logDebug, logWarning, ErrorCodes, SpinDBError, assertValidDatabaseName, assertValidUsername, } from '../../core/error-handler.js';
|
|
16
16
|
import { mariadbBinaryManager } from './binary-manager.js';
|
|
17
17
|
import { getBinaryUrl } from './binary-urls.js';
|
|
@@ -19,44 +19,77 @@ import { fetchAvailableVersions, getLatestVersion } from './hostdb-releases.js';
|
|
|
19
19
|
import { SUPPORTED_MAJOR_VERSIONS, MARIADB_VERSION_MAP } from './version-maps.js';
|
|
20
20
|
import { detectBackupFormat as detectBackupFormatImpl, restoreBackup, parseConnectionString, } from './restore.js';
|
|
21
21
|
import { createBackup } from './backup.js';
|
|
22
|
-
import {
|
|
22
|
+
import { buildMariaDbEnv } from './env.js';
|
|
23
|
+
import { Engine, Platform, } from '../../types/index.js';
|
|
23
24
|
import { parseTSVToQueryResult } from '../../core/query-parser.js';
|
|
24
25
|
import { getLibraryEnv, detectLibraryError } from '../../core/library-env.js';
|
|
25
|
-
const execAsync = promisify(exec);
|
|
26
26
|
const ENGINE = 'mariadb';
|
|
27
27
|
const engineDef = getEngineDefaults(ENGINE);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
28
|
+
async function runMariaDbBinary(binaryPath, args, options = {}) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const proc = spawn(binaryPath, args, {
|
|
31
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
32
|
+
env: buildMariaDbEnv(options.password),
|
|
33
|
+
});
|
|
34
|
+
let stdout = '';
|
|
35
|
+
let stderr = '';
|
|
36
|
+
let settled = false;
|
|
37
|
+
let timeoutId;
|
|
38
|
+
let timeoutError = null;
|
|
39
|
+
const finish = (error) => {
|
|
40
|
+
if (settled)
|
|
41
|
+
return;
|
|
42
|
+
settled = true;
|
|
43
|
+
if (timeoutId) {
|
|
44
|
+
clearTimeout(timeoutId);
|
|
45
|
+
}
|
|
46
|
+
if (error) {
|
|
47
|
+
reject(error);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
resolve({ stdout, stderr });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
if (options.timeout && options.timeout > 0) {
|
|
54
|
+
timeoutId = setTimeout(() => {
|
|
55
|
+
timeoutError = new Error(`mariadb command timed out after ${options.timeout}ms`);
|
|
56
|
+
proc.kill();
|
|
57
|
+
}, options.timeout);
|
|
58
|
+
}
|
|
59
|
+
proc.stdout?.on('data', (data) => {
|
|
60
|
+
stdout += data.toString();
|
|
61
|
+
});
|
|
62
|
+
proc.stderr?.on('data', (data) => {
|
|
63
|
+
stderr += data.toString();
|
|
64
|
+
});
|
|
65
|
+
proc.on('error', (error) => {
|
|
66
|
+
finish(error);
|
|
67
|
+
});
|
|
68
|
+
proc.on('close', (code) => {
|
|
69
|
+
if (timeoutError) {
|
|
70
|
+
finish(timeoutError);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (code === 0) {
|
|
74
|
+
finish();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
finish(new Error(stderr || `mariadb exited with code ${code}`));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
54
80
|
}
|
|
55
81
|
export class MariaDBEngine extends BaseEngine {
|
|
56
82
|
name = ENGINE;
|
|
57
83
|
displayName = 'MariaDB';
|
|
58
84
|
defaultPort = engineDef.defaultPort;
|
|
59
85
|
supportedVersions = SUPPORTED_MAJOR_VERSIONS;
|
|
86
|
+
async getLocalAdminAuth(containerName) {
|
|
87
|
+
const savedCreds = await loadCredentials(containerName, Engine.MariaDB, getDefaultUsername(Engine.MariaDB));
|
|
88
|
+
return {
|
|
89
|
+
user: savedCreds?.username || engineDef.superuser,
|
|
90
|
+
password: savedCreds?.password,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
60
93
|
async fetchAvailableVersions() {
|
|
61
94
|
return fetchAvailableVersions();
|
|
62
95
|
}
|
|
@@ -280,17 +313,11 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
280
313
|
const libraryEnv = getLibraryEnv(binPath);
|
|
281
314
|
if (isWindows()) {
|
|
282
315
|
proc = spawn(mysqld, args, {
|
|
283
|
-
stdio: ['ignore', '
|
|
316
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
284
317
|
detached: true,
|
|
285
318
|
windowsHide: true,
|
|
286
319
|
env: { ...process.env, ...libraryEnv },
|
|
287
320
|
});
|
|
288
|
-
proc.stdout?.on('data', (data) => {
|
|
289
|
-
logDebug(`mariadbd stdout: ${data.toString()}`);
|
|
290
|
-
});
|
|
291
|
-
proc.stderr?.on('data', (data) => {
|
|
292
|
-
logDebug(`mariadbd stderr: ${data.toString()}`);
|
|
293
|
-
});
|
|
294
321
|
proc.unref();
|
|
295
322
|
}
|
|
296
323
|
else {
|
|
@@ -335,7 +362,10 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
335
362
|
attempts++;
|
|
336
363
|
try {
|
|
337
364
|
const mysqladmin = await this.getMysqladminPath();
|
|
338
|
-
|
|
365
|
+
const auth = await this.getLocalAdminAuth(container.name);
|
|
366
|
+
await runMariaDbBinary(mysqladmin, ['-h', '127.0.0.1', '-P', String(port), '-u', auth.user, 'ping'], {
|
|
367
|
+
password: auth.password,
|
|
368
|
+
});
|
|
339
369
|
if (settled)
|
|
340
370
|
return;
|
|
341
371
|
settled = true;
|
|
@@ -393,22 +423,26 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
393
423
|
async stop(container) {
|
|
394
424
|
const { name, port } = container;
|
|
395
425
|
const pidFile = paths.getContainerPidPath(name, { engine: ENGINE });
|
|
426
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
396
427
|
logDebug(`Stopping MariaDB container "${name}" on port ${port}`);
|
|
397
428
|
const pid = await this.getValidatedPid(pidFile);
|
|
398
429
|
if (pid === null) {
|
|
399
430
|
logDebug('No valid PID, checking if MariaDB is responding on port');
|
|
400
431
|
try {
|
|
401
432
|
const mysqladmin = await this.getMysqladminPath();
|
|
402
|
-
await
|
|
433
|
+
await runMariaDbBinary(mysqladmin, ['-h', '127.0.0.1', '-P', String(port), '-u', auth.user, 'ping'], {
|
|
434
|
+
timeout: 2000,
|
|
435
|
+
password: auth.password,
|
|
436
|
+
});
|
|
403
437
|
logWarning(`MariaDB responding on port ${port} but no valid PID file`);
|
|
404
|
-
await this.gracefulShutdown(port);
|
|
438
|
+
await this.gracefulShutdown(port, auth);
|
|
405
439
|
}
|
|
406
440
|
catch {
|
|
407
441
|
logDebug('MariaDB not responding, nothing to stop');
|
|
408
442
|
}
|
|
409
443
|
return;
|
|
410
444
|
}
|
|
411
|
-
const gracefulSuccess = await this.gracefulShutdown(port, pid);
|
|
445
|
+
const gracefulSuccess = await this.gracefulShutdown(port, auth, pid);
|
|
412
446
|
if (gracefulSuccess) {
|
|
413
447
|
await this.cleanupPidFile(pidFile);
|
|
414
448
|
logDebug('MariaDB stopped gracefully');
|
|
@@ -447,11 +481,14 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
447
481
|
return null;
|
|
448
482
|
}
|
|
449
483
|
}
|
|
450
|
-
async gracefulShutdown(port, pid, timeoutMs = 10000) {
|
|
484
|
+
async gracefulShutdown(port, auth, pid, timeoutMs = 10000) {
|
|
451
485
|
try {
|
|
452
486
|
const mysqladmin = await this.getMysqladminPath();
|
|
453
487
|
logDebug('Attempting mysqladmin shutdown');
|
|
454
|
-
await
|
|
488
|
+
await runMariaDbBinary(mysqladmin, ['-h', '127.0.0.1', '-P', String(port), '-u', auth.user, 'shutdown'], {
|
|
489
|
+
timeout: 5000,
|
|
490
|
+
password: auth.password,
|
|
491
|
+
});
|
|
455
492
|
}
|
|
456
493
|
catch (error) {
|
|
457
494
|
const e = error;
|
|
@@ -545,7 +582,10 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
545
582
|
}
|
|
546
583
|
try {
|
|
547
584
|
const mysqladmin = await this.getMysqladminPath();
|
|
548
|
-
|
|
585
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
586
|
+
await runMariaDbBinary(mysqladmin, ['-h', '127.0.0.1', '-P', String(port), '-u', auth.user, 'ping'], {
|
|
587
|
+
password: auth.password,
|
|
588
|
+
});
|
|
549
589
|
return { running: true, message: 'MariaDB is running' };
|
|
550
590
|
}
|
|
551
591
|
catch {
|
|
@@ -556,16 +596,19 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
556
596
|
return detectBackupFormatImpl(filePath);
|
|
557
597
|
}
|
|
558
598
|
async restore(container, backupPath, options = {}) {
|
|
559
|
-
const { port, version } = container;
|
|
599
|
+
const { name, port, version } = container;
|
|
560
600
|
const database = options.database || container.database;
|
|
561
601
|
const binPath = this.getBinaryPath(version);
|
|
602
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
562
603
|
if (options.createDatabase !== false) {
|
|
563
604
|
await this.createDatabase(container, database);
|
|
564
605
|
}
|
|
565
606
|
return restoreBackup(backupPath, {
|
|
607
|
+
containerName: name,
|
|
566
608
|
port,
|
|
567
609
|
database,
|
|
568
|
-
user:
|
|
610
|
+
user: auth.user,
|
|
611
|
+
password: auth.password,
|
|
569
612
|
createDatabase: false,
|
|
570
613
|
validateVersion: options.validateVersion !== false,
|
|
571
614
|
binPath,
|
|
@@ -591,26 +634,38 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
591
634
|
' spindb engines download mariadb');
|
|
592
635
|
}
|
|
593
636
|
async connect(container, database) {
|
|
594
|
-
const { port } = container;
|
|
637
|
+
const { name, port } = container;
|
|
595
638
|
const db = database || container.database || 'mysql';
|
|
596
639
|
const mysql = await this.getMariadbClientPath();
|
|
640
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
597
641
|
const spawnOptions = {
|
|
598
642
|
stdio: 'inherit',
|
|
599
|
-
|
|
643
|
+
env: buildMariaDbEnv(auth.password),
|
|
600
644
|
};
|
|
601
645
|
return new Promise((resolve, reject) => {
|
|
602
|
-
const proc = spawn(mysql, ['-h', '127.0.0.1', '-P', String(port), '-u',
|
|
646
|
+
const proc = spawn(mysql, ['-h', '127.0.0.1', '-P', String(port), '-u', auth.user, db], spawnOptions);
|
|
603
647
|
proc.on('error', reject);
|
|
604
648
|
proc.on('close', () => resolve());
|
|
605
649
|
});
|
|
606
650
|
}
|
|
607
651
|
async createDatabase(container, database) {
|
|
608
652
|
assertValidDatabaseName(database);
|
|
609
|
-
const { port } = container;
|
|
653
|
+
const { name, port } = container;
|
|
610
654
|
const mysql = await this.getMariadbClientPath();
|
|
655
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
611
656
|
try {
|
|
612
|
-
|
|
613
|
-
|
|
657
|
+
await runMariaDbBinary(mysql, [
|
|
658
|
+
'-h',
|
|
659
|
+
'127.0.0.1',
|
|
660
|
+
'-P',
|
|
661
|
+
String(port),
|
|
662
|
+
'-u',
|
|
663
|
+
auth.user,
|
|
664
|
+
'-e',
|
|
665
|
+
`CREATE DATABASE IF NOT EXISTS \`${database}\``,
|
|
666
|
+
], {
|
|
667
|
+
password: auth.password,
|
|
668
|
+
});
|
|
614
669
|
}
|
|
615
670
|
catch (error) {
|
|
616
671
|
const err = error;
|
|
@@ -621,11 +676,22 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
621
676
|
}
|
|
622
677
|
async dropDatabase(container, database) {
|
|
623
678
|
assertValidDatabaseName(database);
|
|
624
|
-
const { port } = container;
|
|
679
|
+
const { name, port } = container;
|
|
625
680
|
const mysql = await this.getMariadbClientPath();
|
|
681
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
626
682
|
try {
|
|
627
|
-
|
|
628
|
-
|
|
683
|
+
await runMariaDbBinary(mysql, [
|
|
684
|
+
'-h',
|
|
685
|
+
'127.0.0.1',
|
|
686
|
+
'-P',
|
|
687
|
+
String(port),
|
|
688
|
+
'-u',
|
|
689
|
+
auth.user,
|
|
690
|
+
'-e',
|
|
691
|
+
`DROP DATABASE IF EXISTS \`${database}\``,
|
|
692
|
+
], {
|
|
693
|
+
password: auth.password,
|
|
694
|
+
});
|
|
629
695
|
}
|
|
630
696
|
catch (error) {
|
|
631
697
|
const err = error;
|
|
@@ -635,12 +701,25 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
635
701
|
}
|
|
636
702
|
}
|
|
637
703
|
async getDatabaseSize(container) {
|
|
638
|
-
const { port, database } = container;
|
|
704
|
+
const { name, port, database } = container;
|
|
639
705
|
const db = database || 'mysql';
|
|
640
706
|
assertValidDatabaseName(db);
|
|
641
707
|
try {
|
|
642
708
|
const mysql = await this.getMariadbClientPath();
|
|
643
|
-
const
|
|
709
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
710
|
+
const { stdout } = await runMariaDbBinary(mysql, [
|
|
711
|
+
'-h',
|
|
712
|
+
'127.0.0.1',
|
|
713
|
+
'-P',
|
|
714
|
+
String(port),
|
|
715
|
+
'-u',
|
|
716
|
+
auth.user,
|
|
717
|
+
'-N',
|
|
718
|
+
'-e',
|
|
719
|
+
`SELECT COALESCE(SUM(data_length + index_length), 0) FROM information_schema.tables WHERE table_schema = '${db}'`,
|
|
720
|
+
], {
|
|
721
|
+
password: auth.password,
|
|
722
|
+
});
|
|
644
723
|
const size = parseInt(stdout.trim(), 10);
|
|
645
724
|
return isNaN(size) ? null : size;
|
|
646
725
|
}
|
|
@@ -651,26 +730,6 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
651
730
|
async dumpFromConnectionString(connectionString, outputPath) {
|
|
652
731
|
const dumpPath = await this.getDumpPath();
|
|
653
732
|
const { host, port, user, password, database } = parseConnectionString(connectionString);
|
|
654
|
-
if (isWindows()) {
|
|
655
|
-
const cmd = `"${dumpPath}" -h ${host} -P ${port} -u ${user} --result-file "${outputPath}" ${database}`;
|
|
656
|
-
const execOptions = {};
|
|
657
|
-
if (password) {
|
|
658
|
-
execOptions.env = { ...process.env, MYSQL_PWD: password };
|
|
659
|
-
}
|
|
660
|
-
try {
|
|
661
|
-
logDebug('Executing mariadb-dump command', { cmd });
|
|
662
|
-
await execAsync(cmd, execOptions);
|
|
663
|
-
return {
|
|
664
|
-
filePath: outputPath,
|
|
665
|
-
stdout: '',
|
|
666
|
-
stderr: '',
|
|
667
|
-
code: 0,
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
catch (error) {
|
|
671
|
-
throw new Error(error.message);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
733
|
const args = [
|
|
675
734
|
'-h',
|
|
676
735
|
host,
|
|
@@ -684,8 +743,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
684
743
|
];
|
|
685
744
|
const spawnOptions = {
|
|
686
745
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
687
|
-
|
|
688
|
-
env: password ? { ...process.env, MYSQL_PWD: password } : process.env,
|
|
746
|
+
env: buildMariaDbEnv(password),
|
|
689
747
|
};
|
|
690
748
|
return new Promise((resolve, reject) => {
|
|
691
749
|
const proc = spawn(dumpPath, args, spawnOptions);
|
|
@@ -725,26 +783,51 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
725
783
|
}
|
|
726
784
|
async terminateConnections(container, database) {
|
|
727
785
|
assertValidDatabaseName(database);
|
|
728
|
-
const { port } = container;
|
|
786
|
+
const { name, port } = container;
|
|
729
787
|
const mysql = await this.getMariadbClientPath();
|
|
788
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
730
789
|
// Get all connection IDs for the target database and kill them
|
|
731
790
|
// We need to do this in two steps since MariaDB doesn't support subqueries in KILL
|
|
732
|
-
const
|
|
791
|
+
const getIdsArgs = [
|
|
792
|
+
mysql,
|
|
793
|
+
'-h',
|
|
794
|
+
'127.0.0.1',
|
|
795
|
+
'-P',
|
|
796
|
+
String(port),
|
|
797
|
+
'-u',
|
|
798
|
+
auth.user,
|
|
799
|
+
'-N',
|
|
800
|
+
'-B',
|
|
801
|
+
'-e',
|
|
802
|
+
`SELECT ID FROM information_schema.PROCESSLIST WHERE DB = '${database}' AND ID != CONNECTION_ID()`,
|
|
803
|
+
];
|
|
733
804
|
try {
|
|
734
|
-
const { stdout } = await
|
|
805
|
+
const { stdout } = await runMariaDbBinary(getIdsArgs[0], getIdsArgs.slice(1), {
|
|
806
|
+
password: auth.password,
|
|
807
|
+
});
|
|
735
808
|
const lines = stdout
|
|
736
809
|
.trim()
|
|
737
810
|
.split('\n')
|
|
738
811
|
.filter((l) => l.trim());
|
|
739
|
-
// Skip header row if present
|
|
740
812
|
const ids = lines
|
|
741
|
-
.slice(1)
|
|
742
813
|
.map((l) => l.trim())
|
|
743
814
|
.filter((l) => /^\d+$/.test(l));
|
|
744
815
|
for (const id of ids) {
|
|
745
|
-
const
|
|
816
|
+
const killArgs = [
|
|
817
|
+
mysql,
|
|
818
|
+
'-h',
|
|
819
|
+
'127.0.0.1',
|
|
820
|
+
'-P',
|
|
821
|
+
String(port),
|
|
822
|
+
'-u',
|
|
823
|
+
auth.user,
|
|
824
|
+
'-e',
|
|
825
|
+
`KILL CONNECTION ${id}`,
|
|
826
|
+
];
|
|
746
827
|
try {
|
|
747
|
-
await
|
|
828
|
+
await runMariaDbBinary(killArgs[0], killArgs.slice(1), {
|
|
829
|
+
password: auth.password,
|
|
830
|
+
});
|
|
748
831
|
}
|
|
749
832
|
catch {
|
|
750
833
|
// Connection may already be gone
|
|
@@ -756,38 +839,25 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
756
839
|
}
|
|
757
840
|
}
|
|
758
841
|
async runScript(container, options) {
|
|
759
|
-
const { port } = container;
|
|
842
|
+
const { name, port } = container;
|
|
760
843
|
const db = options.database || container.database || 'mysql';
|
|
761
844
|
assertValidDatabaseName(db);
|
|
762
845
|
const mysql = await this.getMariadbClientPath();
|
|
763
|
-
|
|
764
|
-
const cmd = buildWindowsMariadbCommand(mysql, port, engineDef.superuser, db, options);
|
|
765
|
-
try {
|
|
766
|
-
const { stdout, stderr } = await execAsync(cmd);
|
|
767
|
-
if (stdout)
|
|
768
|
-
process.stdout.write(stdout);
|
|
769
|
-
if (stderr)
|
|
770
|
-
process.stderr.write(stderr);
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
catch (error) {
|
|
774
|
-
const err = error;
|
|
775
|
-
throw new Error(`mariadb client failed: ${err.message}`);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
846
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
778
847
|
const args = [
|
|
779
848
|
'-h',
|
|
780
849
|
'127.0.0.1',
|
|
781
850
|
'-P',
|
|
782
851
|
String(port),
|
|
783
852
|
'-u',
|
|
784
|
-
|
|
853
|
+
auth.user,
|
|
785
854
|
db,
|
|
786
855
|
];
|
|
787
856
|
if (options.sql) {
|
|
788
857
|
args.push('-e', options.sql);
|
|
789
858
|
const spawnOptions = {
|
|
790
859
|
stdio: 'inherit',
|
|
860
|
+
env: buildMariaDbEnv(auth.password),
|
|
791
861
|
};
|
|
792
862
|
return new Promise((resolve, reject) => {
|
|
793
863
|
const proc = spawn(mysql, args, spawnOptions);
|
|
@@ -805,6 +875,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
805
875
|
else if (options.file) {
|
|
806
876
|
const spawnOptions = {
|
|
807
877
|
stdio: ['pipe', 'inherit', 'inherit'],
|
|
878
|
+
env: buildMariaDbEnv(auth.password),
|
|
808
879
|
};
|
|
809
880
|
return new Promise((resolve, reject) => {
|
|
810
881
|
const fileStream = createReadStream(options.file);
|
|
@@ -830,12 +901,17 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
830
901
|
}
|
|
831
902
|
}
|
|
832
903
|
async executeQuery(container, query, options) {
|
|
833
|
-
const { port } = container;
|
|
904
|
+
const { name, port } = container;
|
|
834
905
|
const db = options?.database || container.database || 'mysql';
|
|
835
906
|
assertValidDatabaseName(db);
|
|
836
907
|
const mariadb = await this.getMariadbClientPath();
|
|
837
908
|
const host = options?.host ?? '127.0.0.1';
|
|
838
|
-
const
|
|
909
|
+
const localAuth = !options?.username &&
|
|
910
|
+
!options?.password &&
|
|
911
|
+
(host === '127.0.0.1' || host === 'localhost')
|
|
912
|
+
? await this.getLocalAdminAuth(name)
|
|
913
|
+
: null;
|
|
914
|
+
const user = options?.username || localAuth?.user || engineDef.superuser;
|
|
839
915
|
// Use -B (batch mode) for tab-separated output
|
|
840
916
|
const args = [
|
|
841
917
|
'-h',
|
|
@@ -853,9 +929,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
853
929
|
args.push('--ssl');
|
|
854
930
|
}
|
|
855
931
|
// Pass password via env to avoid exposing it in process listings
|
|
856
|
-
const env = options?.password
|
|
857
|
-
? { ...process.env, MYSQL_PWD: options.password }
|
|
858
|
-
: process.env;
|
|
932
|
+
const env = buildMariaDbEnv(options?.password ?? localAuth?.password);
|
|
859
933
|
return new Promise((resolve, reject) => {
|
|
860
934
|
const proc = spawn(mariadb, args, {
|
|
861
935
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -885,8 +959,9 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
885
959
|
* (information_schema, mysql, performance_schema, sys).
|
|
886
960
|
*/
|
|
887
961
|
async listDatabases(container) {
|
|
888
|
-
const { port } = container;
|
|
962
|
+
const { name, port } = container;
|
|
889
963
|
const mariadb = await this.getMariadbClientPath();
|
|
964
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
890
965
|
// Query for all non-system databases
|
|
891
966
|
const sql = `SHOW DATABASES WHERE \`Database\` NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')`;
|
|
892
967
|
const args = [
|
|
@@ -895,7 +970,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
895
970
|
'-P',
|
|
896
971
|
String(port),
|
|
897
972
|
'-u',
|
|
898
|
-
|
|
973
|
+
auth.user,
|
|
899
974
|
'-N', // Skip column names
|
|
900
975
|
'-B', // Batch mode (no formatting)
|
|
901
976
|
'-e',
|
|
@@ -904,6 +979,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
904
979
|
return new Promise((resolve, reject) => {
|
|
905
980
|
const proc = spawn(mariadb, args, {
|
|
906
981
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
982
|
+
env: buildMariaDbEnv(auth.password),
|
|
907
983
|
});
|
|
908
984
|
let stdout = '';
|
|
909
985
|
let stderr = '';
|
|
@@ -932,10 +1008,11 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
932
1008
|
async createUser(container, options) {
|
|
933
1009
|
const { username, password, database } = options;
|
|
934
1010
|
assertValidUsername(username);
|
|
935
|
-
const { port } = container;
|
|
1011
|
+
const { name, port } = container;
|
|
936
1012
|
const db = database || container.database || 'mysql';
|
|
937
1013
|
assertValidDatabaseName(db);
|
|
938
1014
|
const mariadb = await this.getMariadbClientPath();
|
|
1015
|
+
const auth = await this.getLocalAdminAuth(name);
|
|
939
1016
|
// Check if NO_BACKSLASH_ESCAPES is enabled — if so, only escape single quotes
|
|
940
1017
|
let noBackslashEscapes = false;
|
|
941
1018
|
try {
|
|
@@ -945,7 +1022,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
945
1022
|
'-P',
|
|
946
1023
|
String(port),
|
|
947
1024
|
'-u',
|
|
948
|
-
|
|
1025
|
+
auth.user,
|
|
949
1026
|
'-N',
|
|
950
1027
|
'-B',
|
|
951
1028
|
'-e',
|
|
@@ -954,6 +1031,7 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
954
1031
|
const modeResult = await new Promise((resolve, reject) => {
|
|
955
1032
|
const proc = spawn(mariadb, modeArgs, {
|
|
956
1033
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1034
|
+
env: buildMariaDbEnv(auth.password),
|
|
957
1035
|
});
|
|
958
1036
|
let stdout = '';
|
|
959
1037
|
proc.stdout?.on('data', (data) => {
|
|
@@ -983,11 +1061,12 @@ export class MariaDBEngine extends BaseEngine {
|
|
|
983
1061
|
'-P',
|
|
984
1062
|
String(port),
|
|
985
1063
|
'-u',
|
|
986
|
-
|
|
1064
|
+
auth.user,
|
|
987
1065
|
];
|
|
988
1066
|
await new Promise((resolve, reject) => {
|
|
989
1067
|
const proc = spawn(mariadb, args, {
|
|
990
1068
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1069
|
+
env: buildMariaDbEnv(auth.password),
|
|
991
1070
|
});
|
|
992
1071
|
let stderr = '';
|
|
993
1072
|
proc.stderr?.on('data', (data) => {
|