spindb 0.46.4 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/cli/commands/info.js +12 -2
  2. package/dist/cli/commands/info.js.map +1 -1
  3. package/dist/cli/commands/link.js +6 -0
  4. package/dist/cli/commands/link.js.map +1 -1
  5. package/dist/cli/commands/list.js +4 -1
  6. package/dist/cli/commands/list.js.map +1 -1
  7. package/dist/cli/commands/menu/container-handlers.js +25 -7
  8. package/dist/cli/commands/menu/container-handlers.js.map +1 -1
  9. package/dist/config/backup-formats.js +11 -11
  10. package/dist/config/backup-formats.js.map +1 -1
  11. package/dist/config/version.js +1 -1
  12. package/dist/core/credential-manager.js +34 -11
  13. package/dist/core/credential-manager.js.map +1 -1
  14. package/dist/core/query-parser.js +55 -1
  15. package/dist/core/query-parser.js.map +1 -1
  16. package/dist/core/remote-container.js +11 -0
  17. package/dist/core/remote-container.js.map +1 -1
  18. package/dist/engines/clickhouse/backup.js +42 -10
  19. package/dist/engines/clickhouse/backup.js.map +1 -1
  20. package/dist/engines/clickhouse/restore.js +41 -10
  21. package/dist/engines/clickhouse/restore.js.map +1 -1
  22. package/dist/engines/cockroachdb/backup.js +18 -22
  23. package/dist/engines/cockroachdb/backup.js.map +1 -1
  24. package/dist/engines/cockroachdb/cli-utils.js +66 -0
  25. package/dist/engines/cockroachdb/cli-utils.js.map +1 -1
  26. package/dist/engines/cockroachdb/index.js +199 -116
  27. package/dist/engines/cockroachdb/index.js.map +1 -1
  28. package/dist/engines/cockroachdb/restore.js +19 -26
  29. package/dist/engines/cockroachdb/restore.js.map +1 -1
  30. package/dist/engines/couchdb/backup.js +13 -4
  31. package/dist/engines/couchdb/backup.js.map +1 -1
  32. package/dist/engines/couchdb/index.js +93 -25
  33. package/dist/engines/couchdb/index.js.map +1 -1
  34. package/dist/engines/couchdb/restore.js +15 -4
  35. package/dist/engines/couchdb/restore.js.map +1 -1
  36. package/dist/engines/ferretdb/backup.js +88 -91
  37. package/dist/engines/ferretdb/backup.js.map +1 -1
  38. package/dist/engines/ferretdb/index.js +179 -226
  39. package/dist/engines/ferretdb/index.js.map +1 -1
  40. package/dist/engines/ferretdb/restore.js +223 -20
  41. package/dist/engines/ferretdb/restore.js.map +1 -1
  42. package/dist/engines/influxdb/api-client.js +1 -1
  43. package/dist/engines/influxdb/api-client.js.map +1 -1
  44. package/dist/engines/influxdb/backup.js +25 -5
  45. package/dist/engines/influxdb/backup.js.map +1 -1
  46. package/dist/engines/influxdb/index.js +165 -43
  47. package/dist/engines/influxdb/index.js.map +1 -1
  48. package/dist/engines/influxdb/restore.js +22 -2
  49. package/dist/engines/influxdb/restore.js.map +1 -1
  50. package/dist/engines/mariadb/backup.js +24 -15
  51. package/dist/engines/mariadb/backup.js.map +1 -1
  52. package/dist/engines/mariadb/env.js +11 -0
  53. package/dist/engines/mariadb/env.js.map +1 -0
  54. package/dist/engines/mariadb/index.js +192 -113
  55. package/dist/engines/mariadb/index.js.map +1 -1
  56. package/dist/engines/mariadb/restore.js +21 -5
  57. package/dist/engines/mariadb/restore.js.map +1 -1
  58. package/dist/engines/meilisearch/backup.js +8 -4
  59. package/dist/engines/meilisearch/backup.js.map +1 -1
  60. package/dist/engines/meilisearch/index.js +55 -58
  61. package/dist/engines/meilisearch/index.js.map +1 -1
  62. package/dist/engines/mongo-uri.js +8 -0
  63. package/dist/engines/mongo-uri.js.map +1 -0
  64. package/dist/engines/mongodb/backup.js +62 -13
  65. package/dist/engines/mongodb/backup.js.map +1 -1
  66. package/dist/engines/mongodb/index.js +170 -108
  67. package/dist/engines/mongodb/index.js.map +1 -1
  68. package/dist/engines/mongodb/restore.js +21 -1
  69. package/dist/engines/mongodb/restore.js.map +1 -1
  70. package/dist/engines/mysql/backup.js +24 -7
  71. package/dist/engines/mysql/backup.js.map +1 -1
  72. package/dist/engines/mysql/index.js +154 -89
  73. package/dist/engines/mysql/index.js.map +1 -1
  74. package/dist/engines/mysql/restore.js +14 -4
  75. package/dist/engines/mysql/restore.js.map +1 -1
  76. package/dist/engines/postgresql/backup.js +9 -2
  77. package/dist/engines/postgresql/backup.js.map +1 -1
  78. package/dist/engines/postgresql/index.js +10 -4
  79. package/dist/engines/postgresql/index.js.map +1 -1
  80. package/dist/engines/postgresql/restore.js +7 -3
  81. package/dist/engines/postgresql/restore.js.map +1 -1
  82. package/dist/engines/qdrant/backup.js +5 -1
  83. package/dist/engines/qdrant/backup.js.map +1 -1
  84. package/dist/engines/qdrant/index.js +31 -2
  85. package/dist/engines/qdrant/index.js.map +1 -1
  86. package/dist/engines/qdrant/restore.js +5 -3
  87. package/dist/engines/qdrant/restore.js.map +1 -1
  88. package/dist/engines/questdb/auth.js +26 -0
  89. package/dist/engines/questdb/auth.js.map +1 -0
  90. package/dist/engines/questdb/backup.js +10 -8
  91. package/dist/engines/questdb/backup.js.map +1 -1
  92. package/dist/engines/questdb/index.js +16 -8
  93. package/dist/engines/questdb/index.js.map +1 -1
  94. package/dist/engines/questdb/restore.js +7 -5
  95. package/dist/engines/questdb/restore.js.map +1 -1
  96. package/dist/engines/redis/backup.js +48 -15
  97. package/dist/engines/redis/backup.js.map +1 -1
  98. package/dist/engines/redis/index.js +45 -12
  99. package/dist/engines/redis/index.js.map +1 -1
  100. package/dist/engines/redis/restore.js +21 -2
  101. package/dist/engines/redis/restore.js.map +1 -1
  102. package/dist/engines/surrealdb/auth.js +98 -0
  103. package/dist/engines/surrealdb/auth.js.map +1 -0
  104. package/dist/engines/surrealdb/backup.js +72 -9
  105. package/dist/engines/surrealdb/backup.js.map +1 -1
  106. package/dist/engines/surrealdb/index.js +84 -144
  107. package/dist/engines/surrealdb/index.js.map +1 -1
  108. package/dist/engines/surrealdb/restore.js +32 -31
  109. package/dist/engines/surrealdb/restore.js.map +1 -1
  110. package/dist/engines/typedb/backup.js +9 -1
  111. package/dist/engines/typedb/backup.js.map +1 -1
  112. package/dist/engines/typedb/cli-utils.js +3 -3
  113. package/dist/engines/typedb/cli-utils.js.map +1 -1
  114. package/dist/engines/typedb/index.js +9 -2
  115. package/dist/engines/typedb/index.js.map +1 -1
  116. package/dist/engines/typedb/restore.js +19 -7
  117. package/dist/engines/typedb/restore.js.map +1 -1
  118. package/dist/engines/valkey/backup.js +37 -13
  119. package/dist/engines/valkey/backup.js.map +1 -1
  120. package/dist/engines/valkey/index.js +207 -58
  121. package/dist/engines/valkey/index.js.map +1 -1
  122. package/dist/engines/valkey/restore.js +21 -2
  123. package/dist/engines/valkey/restore.js.map +1 -1
  124. package/dist/engines/weaviate/backup.js +7 -2
  125. package/dist/engines/weaviate/backup.js.map +1 -1
  126. package/dist/types/index.js.map +1 -1
  127. 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, getWindowsSpawnOptions, } from '../../core/platform-service.js';
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 { Platform, } from '../../types/index.js';
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
- // Build a Windows-safe mariadb command string for either a file or inline SQL.
29
- export function buildWindowsMariadbCommand(mysqlPath, port, user, db, options) {
30
- if (!options.file && !options.sql) {
31
- throw new Error('Either file or sql option must be provided');
32
- }
33
- let cmd = `"${mysqlPath}" -h 127.0.0.1 -P ${port} -u ${user} ${db}`;
34
- if (options.file) {
35
- cmd += ` < "${options.file}"`;
36
- }
37
- else if (options.sql) {
38
- const escaped = options.sql.replace(/"/g, '\\"');
39
- cmd += ` -e "${escaped}"`;
40
- }
41
- return cmd;
42
- }
43
- // Build a platform-safe mariadb command string with SQL inline.
44
- export function buildMariadbInlineCommand(mysqlPath, port, user, sql, options = {}) {
45
- const dbArg = options.database ? ` ${options.database}` : '';
46
- if (isWindows()) {
47
- const escaped = sql.replace(/"/g, '\\"');
48
- return `"${mysqlPath}" -h 127.0.0.1 -P ${port} -u ${user}${dbArg} -e "${escaped}"`;
49
- }
50
- else {
51
- const escaped = sql.replace(/'/g, "'\\''");
52
- return `"${mysqlPath}" -h 127.0.0.1 -P ${port} -u ${user}${dbArg} -e '${escaped}'`;
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', 'pipe', 'pipe'],
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
- await execAsync(`"${mysqladmin}" -h 127.0.0.1 -P ${port} -u root ping`);
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 execAsync(`"${mysqladmin}" -h 127.0.0.1 -P ${port} -u root ping`, { timeout: 2000 });
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 execAsync(`"${mysqladmin}" -h 127.0.0.1 -P ${port} -u root shutdown`, { timeout: 5000 });
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
- await execAsync(`"${mysqladmin}" -h 127.0.0.1 -P ${port} -u root ping`);
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: engineDef.superuser,
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
- ...getWindowsSpawnOptions(),
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', engineDef.superuser, db], spawnOptions);
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
- const cmd = buildMariadbInlineCommand(mysql, port, engineDef.superuser, `CREATE DATABASE IF NOT EXISTS \`${database}\``);
613
- await execAsync(cmd);
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
- const cmd = buildMariadbInlineCommand(mysql, port, engineDef.superuser, `DROP DATABASE IF EXISTS \`${database}\``);
628
- await execAsync(cmd);
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 { stdout } = await execAsync(`"${mysql}" -h 127.0.0.1 -P ${port} -u ${engineDef.superuser} -N -e "SELECT COALESCE(SUM(data_length + index_length), 0) FROM information_schema.tables WHERE table_schema = '${db}'"`);
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
- ...getWindowsSpawnOptions(),
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 getIdsCmd = buildMariadbInlineCommand(mysql, port, engineDef.superuser, `SELECT ID FROM information_schema.PROCESSLIST WHERE DB = '${database}' AND ID != CONNECTION_ID()`);
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 execAsync(getIdsCmd);
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 killCmd = buildMariadbInlineCommand(mysql, port, engineDef.superuser, `KILL CONNECTION ${id}`);
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 execAsync(killCmd);
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
- if (isWindows()) {
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
- engineDef.superuser,
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 user = options?.username || engineDef.superuser;
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
- engineDef.superuser,
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
- engineDef.superuser,
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
- engineDef.superuser,
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) => {