spindb 0.46.5 → 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.
- 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 +179 -226
- 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 +21 -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 +8 -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 +10 -4
- 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 +48 -15
- package/dist/engines/redis/backup.js.map +1 -1
- package/dist/engines/redis/index.js +31 -8
- package/dist/engines/redis/index.js.map +1 -1
- package/dist/engines/redis/restore.js +21 -2
- 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
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Uses PostgreSQL wire protocol for client connections
|
|
11
11
|
* - Single binary: `cockroach` (handles server, sql client, and admin tasks)
|
|
12
12
|
* - Default database: `defaultdb`
|
|
13
|
-
* - Default user: `root`
|
|
13
|
+
* - Default local admin user: `root` authenticated via generated client cert
|
|
14
14
|
*/
|
|
15
15
|
import { spawn } from 'child_process';
|
|
16
16
|
import { existsSync } from 'fs';
|
|
@@ -30,10 +30,80 @@ import { normalizeVersion, SUPPORTED_MAJOR_VERSIONS, COCKROACHDB_VERSION_MAP, }
|
|
|
30
30
|
import { fetchAvailableVersions as fetchHostdbVersions } from './hostdb-releases.js';
|
|
31
31
|
import { detectBackupFormat as detectBackupFormatImpl, restoreBackup, } from './restore.js';
|
|
32
32
|
import { createBackup } from './backup.js';
|
|
33
|
-
import { validateCockroachIdentifier, escapeCockroachIdentifier, escapeSqlValue, parseCsvLine, parseCsvRecords, isInsecureConnection, } from './cli-utils.js';
|
|
33
|
+
import { validateCockroachIdentifier, escapeCockroachIdentifier, escapeSqlValue, parseCsvLine, parseCsvRecords, isInsecureConnection, buildLocalCockroachSqlArgs, buildSecureCockroachConnectionString, buildInsecureCockroachConnectionString, getCockroachCertsDir, getCockroachCaCertPath, getCockroachCaKeyPath, getCockroachClientCertPath, getCockroachClientKeyPath, } from './cli-utils.js';
|
|
34
34
|
import { parseCSVToQueryResult } from '../../core/query-parser.js';
|
|
35
35
|
const ENGINE = 'cockroachdb';
|
|
36
36
|
const engineDef = getEngineDefaults(ENGINE);
|
|
37
|
+
async function runCockroachCommand(cockroachPath, args, spawnOptions = {}) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const proc = spawn(cockroachPath, args, {
|
|
40
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
41
|
+
...spawnOptions,
|
|
42
|
+
});
|
|
43
|
+
let stdout = '';
|
|
44
|
+
let stderr = '';
|
|
45
|
+
proc.stdout?.on('data', (data) => {
|
|
46
|
+
stdout += data.toString();
|
|
47
|
+
});
|
|
48
|
+
proc.stderr?.on('data', (data) => {
|
|
49
|
+
stderr += data.toString();
|
|
50
|
+
});
|
|
51
|
+
proc.on('error', reject);
|
|
52
|
+
proc.on('close', (code) => {
|
|
53
|
+
if (code === 0) {
|
|
54
|
+
resolve({ stdout, stderr });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
reject(new Error(stderr || `cockroach exited with code ${code}`));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async function ensureSecureLocalAssets(cockroachPath, containerName, bindAddress) {
|
|
63
|
+
const certsDir = getCockroachCertsDir(containerName);
|
|
64
|
+
const caKey = getCockroachCaKeyPath(containerName);
|
|
65
|
+
const nodeCert = join(certsDir, 'node.crt');
|
|
66
|
+
const nodeKey = join(certsDir, 'node.key');
|
|
67
|
+
const rootClientCert = getCockroachClientCertPath(containerName, 'root');
|
|
68
|
+
const rootClientKey = getCockroachClientKeyPath(containerName, 'root');
|
|
69
|
+
await mkdir(certsDir, { recursive: true });
|
|
70
|
+
if (!existsSync(join(certsDir, 'ca.crt')) || !existsSync(caKey)) {
|
|
71
|
+
await runCockroachCommand(cockroachPath, [
|
|
72
|
+
'cert',
|
|
73
|
+
'create-ca',
|
|
74
|
+
'--certs-dir',
|
|
75
|
+
certsDir,
|
|
76
|
+
'--ca-key',
|
|
77
|
+
caKey,
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
if (!existsSync(nodeCert) || !existsSync(nodeKey)) {
|
|
81
|
+
const hosts = new Set(['127.0.0.1', 'localhost', '::1']);
|
|
82
|
+
if (bindAddress && bindAddress !== '0.0.0.0' && bindAddress !== '::') {
|
|
83
|
+
hosts.add(bindAddress);
|
|
84
|
+
}
|
|
85
|
+
await runCockroachCommand(cockroachPath, [
|
|
86
|
+
'cert',
|
|
87
|
+
'create-node',
|
|
88
|
+
...Array.from(hosts),
|
|
89
|
+
'--certs-dir',
|
|
90
|
+
certsDir,
|
|
91
|
+
'--ca-key',
|
|
92
|
+
caKey,
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
if (!existsSync(rootClientCert) || !existsSync(rootClientKey)) {
|
|
96
|
+
await runCockroachCommand(cockroachPath, [
|
|
97
|
+
'cert',
|
|
98
|
+
'create-client',
|
|
99
|
+
'root',
|
|
100
|
+
'--certs-dir',
|
|
101
|
+
certsDir,
|
|
102
|
+
'--ca-key',
|
|
103
|
+
caKey,
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
37
107
|
export class CockroachDBEngine extends BaseEngine {
|
|
38
108
|
name = ENGINE;
|
|
39
109
|
displayName = 'CockroachDB';
|
|
@@ -168,11 +238,13 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
168
238
|
const httpPort = port + 1; // HTTP admin UI port
|
|
169
239
|
onProgress?.({ stage: 'starting', message: 'Starting CockroachDB...' });
|
|
170
240
|
logDebug(`Starting CockroachDB with data dir: ${dataDir}`);
|
|
241
|
+
await ensureSecureLocalAssets(cockroachBinary, name, container.bindAddress);
|
|
171
242
|
// CockroachDB start command
|
|
172
|
-
//
|
|
243
|
+
// Local containers run in secure mode with per-container certs.
|
|
173
244
|
const args = [
|
|
174
245
|
'start-single-node',
|
|
175
|
-
'--
|
|
246
|
+
'--certs-dir',
|
|
247
|
+
getCockroachCertsDir(name),
|
|
176
248
|
'--store',
|
|
177
249
|
dataDir,
|
|
178
250
|
'--listen-addr',
|
|
@@ -262,7 +334,7 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
262
334
|
// Windows needs a longer timeout since CockroachDB initialization takes more time
|
|
263
335
|
const timeout = isWindows ? 120000 : 60000;
|
|
264
336
|
logDebug(`Waiting for CockroachDB server to be ready on port ${port}... (timeout: ${timeout}ms)`);
|
|
265
|
-
const ready = await this.waitForReady(port, version, timeout);
|
|
337
|
+
const ready = await this.waitForReady(name, port, version, timeout);
|
|
266
338
|
logDebug(`waitForReady returned: ${ready}`);
|
|
267
339
|
if (!ready) {
|
|
268
340
|
// Clean up the spawned process and PID file before throwing
|
|
@@ -288,7 +360,7 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
288
360
|
};
|
|
289
361
|
}
|
|
290
362
|
// Wait for CockroachDB to be ready
|
|
291
|
-
async waitForReady(port, version, timeoutMs = 60000) {
|
|
363
|
+
async waitForReady(containerName, port, version, timeoutMs = 60000) {
|
|
292
364
|
logDebug(`waitForReady called for port ${port}, version ${version}`);
|
|
293
365
|
const startTime = Date.now();
|
|
294
366
|
const checkInterval = 500;
|
|
@@ -310,14 +382,11 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
310
382
|
attempt++;
|
|
311
383
|
logDebug(`Connection attempt ${attempt}...`);
|
|
312
384
|
try {
|
|
313
|
-
const args =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
'--execute',
|
|
319
|
-
'SELECT 1',
|
|
320
|
-
];
|
|
385
|
+
const args = buildLocalCockroachSqlArgs({
|
|
386
|
+
containerName,
|
|
387
|
+
port,
|
|
388
|
+
});
|
|
389
|
+
args.push('--execute', 'SELECT 1');
|
|
321
390
|
await new Promise((resolve, reject) => {
|
|
322
391
|
const proc = spawn(cockroach, args, {
|
|
323
392
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -403,18 +472,15 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
403
472
|
}
|
|
404
473
|
// Get CockroachDB server status
|
|
405
474
|
async status(container) {
|
|
406
|
-
const { port, version } = container;
|
|
475
|
+
const { name, port, version } = container;
|
|
407
476
|
// Try to connect
|
|
408
477
|
try {
|
|
409
478
|
const cockroach = await this.getCockroachPath(version);
|
|
410
|
-
const args =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
'--execute',
|
|
416
|
-
'SELECT 1',
|
|
417
|
-
];
|
|
479
|
+
const args = buildLocalCockroachSqlArgs({
|
|
480
|
+
containerName: name,
|
|
481
|
+
port,
|
|
482
|
+
});
|
|
483
|
+
args.push('--execute', 'SELECT 1');
|
|
418
484
|
await new Promise((resolve, reject) => {
|
|
419
485
|
const proc = spawn(cockroach, args, {
|
|
420
486
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -452,23 +518,40 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
452
518
|
}
|
|
453
519
|
/**
|
|
454
520
|
* Get connection string
|
|
455
|
-
* Format: postgresql://root@127.0.0.1:PORT/DATABASE?sslmode=
|
|
521
|
+
* Format: postgresql://root@127.0.0.1:PORT/DATABASE?sslmode=verify-full...
|
|
456
522
|
*/
|
|
457
523
|
getConnectionString(container, database) {
|
|
458
|
-
const { port } = container;
|
|
524
|
+
const { name, port } = container;
|
|
459
525
|
const db = database || container.database || 'defaultdb';
|
|
460
|
-
|
|
526
|
+
const legacyInsecureRunning = container.status === 'running' &&
|
|
527
|
+
!existsSync(getCockroachCaCertPath(name));
|
|
528
|
+
if (legacyInsecureRunning) {
|
|
529
|
+
return buildInsecureCockroachConnectionString({
|
|
530
|
+
port,
|
|
531
|
+
database: db,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
return buildSecureCockroachConnectionString({
|
|
535
|
+
containerName: name,
|
|
536
|
+
port,
|
|
537
|
+
database: db,
|
|
538
|
+
});
|
|
461
539
|
}
|
|
462
540
|
// Open cockroach sql interactive shell
|
|
463
541
|
async connect(container, database) {
|
|
464
|
-
const { port, version } = container;
|
|
542
|
+
const { name, port, version } = container;
|
|
465
543
|
const db = database || container.database || 'defaultdb';
|
|
466
544
|
const cockroach = await this.getCockroachPath(version);
|
|
545
|
+
const args = buildLocalCockroachSqlArgs({
|
|
546
|
+
containerName: name,
|
|
547
|
+
port,
|
|
548
|
+
database: db,
|
|
549
|
+
});
|
|
467
550
|
const spawnOptions = {
|
|
468
551
|
stdio: 'inherit',
|
|
469
552
|
};
|
|
470
553
|
return new Promise((resolve, reject) => {
|
|
471
|
-
const proc = spawn(cockroach,
|
|
554
|
+
const proc = spawn(cockroach, args, spawnOptions);
|
|
472
555
|
proc.on('error', reject);
|
|
473
556
|
proc.on('close', () => resolve());
|
|
474
557
|
});
|
|
@@ -477,19 +560,16 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
477
560
|
* Create a new database
|
|
478
561
|
*/
|
|
479
562
|
async createDatabase(container, database) {
|
|
480
|
-
const { port, version } = container;
|
|
563
|
+
const { name, port, version } = container;
|
|
481
564
|
// Validate database identifier to prevent SQL injection
|
|
482
565
|
validateCockroachIdentifier(database, 'database');
|
|
483
566
|
const escapedDb = escapeCockroachIdentifier(database);
|
|
484
567
|
const cockroach = await this.getCockroachPath(version);
|
|
485
|
-
const args =
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
'--execute',
|
|
491
|
-
`CREATE DATABASE IF NOT EXISTS ${escapedDb}`,
|
|
492
|
-
];
|
|
568
|
+
const args = buildLocalCockroachSqlArgs({
|
|
569
|
+
containerName: name,
|
|
570
|
+
port,
|
|
571
|
+
});
|
|
572
|
+
args.push('--execute', `CREATE DATABASE IF NOT EXISTS ${escapedDb}`);
|
|
493
573
|
await new Promise((resolve, reject) => {
|
|
494
574
|
const proc = spawn(cockroach, args, {
|
|
495
575
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -514,7 +594,7 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
514
594
|
* Drop a database
|
|
515
595
|
*/
|
|
516
596
|
async dropDatabase(container, database) {
|
|
517
|
-
const { port, version } = container;
|
|
597
|
+
const { name, port, version } = container;
|
|
518
598
|
// Don't allow dropping system databases
|
|
519
599
|
const systemDatabases = ['defaultdb', 'postgres', 'system'];
|
|
520
600
|
if (systemDatabases.includes(database.toLowerCase())) {
|
|
@@ -524,14 +604,11 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
524
604
|
validateCockroachIdentifier(database, 'database');
|
|
525
605
|
const escapedDb = escapeCockroachIdentifier(database);
|
|
526
606
|
const cockroach = await this.getCockroachPath(version);
|
|
527
|
-
const args =
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
'--execute',
|
|
533
|
-
`DROP DATABASE IF EXISTS ${escapedDb}`,
|
|
534
|
-
];
|
|
607
|
+
const args = buildLocalCockroachSqlArgs({
|
|
608
|
+
containerName: name,
|
|
609
|
+
port,
|
|
610
|
+
});
|
|
611
|
+
args.push('--execute', `DROP DATABASE IF EXISTS ${escapedDb}`);
|
|
535
612
|
await new Promise((resolve, reject) => {
|
|
536
613
|
const proc = spawn(cockroach, args, {
|
|
537
614
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -556,7 +633,7 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
556
633
|
* Rename a database using CockroachDB's native ALTER DATABASE RENAME
|
|
557
634
|
*/
|
|
558
635
|
async renameDatabase(container, oldName, newName) {
|
|
559
|
-
const { port, version } = container;
|
|
636
|
+
const { name, port, version } = container;
|
|
560
637
|
const systemDatabases = ['defaultdb', 'postgres', 'system'];
|
|
561
638
|
if (systemDatabases.includes(oldName.toLowerCase())) {
|
|
562
639
|
throw new Error(`Cannot rename system database: ${oldName}`);
|
|
@@ -569,14 +646,11 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
569
646
|
const escapedOld = escapeCockroachIdentifier(oldName);
|
|
570
647
|
const escapedNew = escapeCockroachIdentifier(newName);
|
|
571
648
|
const cockroach = await this.getCockroachPath(version);
|
|
572
|
-
const args =
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
'--execute',
|
|
578
|
-
`ALTER DATABASE ${escapedOld} RENAME TO ${escapedNew}`,
|
|
579
|
-
];
|
|
649
|
+
const args = buildLocalCockroachSqlArgs({
|
|
650
|
+
containerName: name,
|
|
651
|
+
port,
|
|
652
|
+
});
|
|
653
|
+
args.push('--execute', `ALTER DATABASE ${escapedOld} RENAME TO ${escapedNew}`);
|
|
580
654
|
await new Promise((resolve, reject) => {
|
|
581
655
|
const proc = spawn(cockroach, args, {
|
|
582
656
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -601,7 +675,7 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
601
675
|
* Get the database size in bytes
|
|
602
676
|
*/
|
|
603
677
|
async getDatabaseSize(container) {
|
|
604
|
-
const { port, version, database } = container;
|
|
678
|
+
const { name, port, version, database } = container;
|
|
605
679
|
const db = database || 'defaultdb';
|
|
606
680
|
try {
|
|
607
681
|
const cockroach = await this.getCockroachPath(version);
|
|
@@ -609,17 +683,12 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
609
683
|
// CockroachDB query to get database size
|
|
610
684
|
const query = `SELECT sum(range_size_mb) * 1024 * 1024 as size_bytes FROM [SHOW RANGES FROM DATABASE ${escapeCockroachIdentifier(db)}]`;
|
|
611
685
|
const result = await new Promise((resolve, reject) => {
|
|
612
|
-
const args =
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
db,
|
|
619
|
-
'--execute',
|
|
620
|
-
query,
|
|
621
|
-
'--format=csv',
|
|
622
|
-
];
|
|
686
|
+
const args = buildLocalCockroachSqlArgs({
|
|
687
|
+
containerName: name,
|
|
688
|
+
port,
|
|
689
|
+
database: db,
|
|
690
|
+
});
|
|
691
|
+
args.push('--execute', query, '--format=csv');
|
|
623
692
|
const proc = spawn(cockroach, args, {
|
|
624
693
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
625
694
|
});
|
|
@@ -852,21 +921,17 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
852
921
|
}
|
|
853
922
|
// Run a SQL file or inline SQL statement
|
|
854
923
|
async runScript(container, options) {
|
|
855
|
-
const { port, version } = container;
|
|
924
|
+
const { name, port, version } = container;
|
|
856
925
|
const db = options.database || container.database || 'defaultdb';
|
|
857
926
|
const cockroach = await this.getCockroachPath(version);
|
|
858
927
|
if (options.file) {
|
|
859
928
|
// Run SQL file
|
|
860
|
-
const args =
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
db,
|
|
867
|
-
'--file',
|
|
868
|
-
options.file,
|
|
869
|
-
];
|
|
929
|
+
const args = buildLocalCockroachSqlArgs({
|
|
930
|
+
containerName: name,
|
|
931
|
+
port,
|
|
932
|
+
database: db,
|
|
933
|
+
});
|
|
934
|
+
args.push('--file', options.file);
|
|
870
935
|
await new Promise((resolve, reject) => {
|
|
871
936
|
const proc = spawn(cockroach, args, {
|
|
872
937
|
stdio: 'inherit',
|
|
@@ -887,14 +952,11 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
887
952
|
}
|
|
888
953
|
else if (options.sql) {
|
|
889
954
|
// Run inline SQL via stdin
|
|
890
|
-
const args =
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
'--database',
|
|
896
|
-
db,
|
|
897
|
-
];
|
|
955
|
+
const args = buildLocalCockroachSqlArgs({
|
|
956
|
+
containerName: name,
|
|
957
|
+
port,
|
|
958
|
+
database: db,
|
|
959
|
+
});
|
|
898
960
|
await new Promise((resolve, reject) => {
|
|
899
961
|
const proc = spawn(cockroach, args, {
|
|
900
962
|
stdio: ['pipe', 'inherit', 'inherit'],
|
|
@@ -923,19 +985,27 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
923
985
|
* Execute a SQL query and return structured results
|
|
924
986
|
*/
|
|
925
987
|
async executeQuery(container, query, options) {
|
|
926
|
-
const { port, version } = container;
|
|
988
|
+
const { name, port, version } = container;
|
|
927
989
|
const db = options?.database || container.database || 'defaultdb';
|
|
928
990
|
const cockroach = await this.getCockroachPath(version);
|
|
929
991
|
return new Promise((resolve, reject) => {
|
|
930
|
-
const args =
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
992
|
+
const args = options?.host
|
|
993
|
+
? (() => {
|
|
994
|
+
const username = options.username || 'root';
|
|
995
|
+
const remoteUrl = new URL(`postgresql://${encodeURIComponent(username)}@${options.host}:${port}/${db}`);
|
|
996
|
+
if (options.password) {
|
|
997
|
+
remoteUrl.password = options.password;
|
|
998
|
+
}
|
|
999
|
+
remoteUrl.searchParams.set('sslmode', options.ssl === false ? 'disable' : 'require');
|
|
1000
|
+
return ['sql', '--url', remoteUrl.toString()];
|
|
1001
|
+
})()
|
|
1002
|
+
: buildLocalCockroachSqlArgs({
|
|
1003
|
+
containerName: name,
|
|
1004
|
+
port,
|
|
1005
|
+
database: db,
|
|
1006
|
+
username: options?.username,
|
|
1007
|
+
password: options?.password,
|
|
1008
|
+
});
|
|
939
1009
|
args.push('--execute', query, '--format=csv');
|
|
940
1010
|
const proc = spawn(cockroach, args, {
|
|
941
1011
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -967,18 +1037,14 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
967
1037
|
* List all user databases, excluding system databases (defaultdb, postgres, system).
|
|
968
1038
|
*/
|
|
969
1039
|
async listDatabases(container) {
|
|
970
|
-
const { port, version } = container;
|
|
1040
|
+
const { name, port, version } = container;
|
|
971
1041
|
const cockroach = await this.getCockroachPath(version);
|
|
972
1042
|
return new Promise((resolve, reject) => {
|
|
973
|
-
const args =
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
'--execute',
|
|
979
|
-
`SHOW DATABASES`,
|
|
980
|
-
'--format=csv',
|
|
981
|
-
];
|
|
1043
|
+
const args = buildLocalCockroachSqlArgs({
|
|
1044
|
+
containerName: name,
|
|
1045
|
+
port,
|
|
1046
|
+
});
|
|
1047
|
+
args.push('--execute', `SHOW DATABASES`, '--format=csv');
|
|
982
1048
|
const proc = spawn(cockroach, args, {
|
|
983
1049
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
984
1050
|
});
|
|
@@ -1010,18 +1076,32 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
1010
1076
|
async createUser(container, options) {
|
|
1011
1077
|
const { username, password, database } = options;
|
|
1012
1078
|
assertValidUsername(username);
|
|
1013
|
-
const { port, version } = container;
|
|
1079
|
+
const { name, port, version } = container;
|
|
1014
1080
|
const db = database || container.database || 'defaultdb';
|
|
1015
1081
|
validateCockroachIdentifier(username, 'user');
|
|
1016
1082
|
validateCockroachIdentifier(db, 'database');
|
|
1017
1083
|
const escapedUser = escapeCockroachIdentifier(username);
|
|
1018
1084
|
const escapedDb = escapeCockroachIdentifier(db);
|
|
1019
1085
|
const cockroach = await this.getCockroachPath(version);
|
|
1020
|
-
// CockroachDB
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
1023
|
-
const
|
|
1024
|
-
const
|
|
1086
|
+
// Local CockroachDB containers run with TLS enabled. Bootstrap admin commands
|
|
1087
|
+
// authenticate as root via the generated client certificate, while end users
|
|
1088
|
+
// connect with password-based auth over TLS.
|
|
1089
|
+
const escapedPassword = password.replace(/'/g, "''");
|
|
1090
|
+
const sql = [
|
|
1091
|
+
`CREATE USER IF NOT EXISTS ${escapedUser}`,
|
|
1092
|
+
`ALTER USER ${escapedUser} WITH PASSWORD '${escapedPassword}'`,
|
|
1093
|
+
`GRANT ALL ON DATABASE ${escapedDb} TO ${escapedUser}`,
|
|
1094
|
+
`GRANT ALL ON SCHEMA public TO ${escapedUser}`,
|
|
1095
|
+
`GRANT ALL ON ALL TABLES IN SCHEMA public TO ${escapedUser}`,
|
|
1096
|
+
`GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO ${escapedUser}`,
|
|
1097
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${escapedUser}`,
|
|
1098
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${escapedUser}`,
|
|
1099
|
+
].join('; ') + ';';
|
|
1100
|
+
const args = buildLocalCockroachSqlArgs({
|
|
1101
|
+
containerName: name,
|
|
1102
|
+
port,
|
|
1103
|
+
database: db,
|
|
1104
|
+
});
|
|
1025
1105
|
await new Promise((resolve, reject) => {
|
|
1026
1106
|
const proc = spawn(cockroach, args, {
|
|
1027
1107
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -1042,12 +1122,15 @@ export class CockroachDBEngine extends BaseEngine {
|
|
|
1042
1122
|
proc.stdin?.write(sql);
|
|
1043
1123
|
proc.stdin?.end();
|
|
1044
1124
|
});
|
|
1045
|
-
|
|
1046
|
-
|
|
1125
|
+
const connectionString = buildSecureCockroachConnectionString({
|
|
1126
|
+
containerName: name,
|
|
1127
|
+
port,
|
|
1128
|
+
database: db,
|
|
1129
|
+
username,
|
|
1130
|
+
password,
|
|
1131
|
+
});
|
|
1047
1132
|
return {
|
|
1048
1133
|
username,
|
|
1049
|
-
// CockroachDB insecure mode does not enforce password authentication,
|
|
1050
|
-
// but we return the caller-provided password for credential file consistency.
|
|
1051
1134
|
password,
|
|
1052
1135
|
connectionString,
|
|
1053
1136
|
engine: container.engine,
|