spindb 0.26.2 → 0.27.3
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/README.md +27 -10
- package/cli/commands/backups.ts +6 -17
- package/cli/commands/config.ts +5 -4
- package/cli/commands/engines.ts +95 -43
- package/cli/commands/info.ts +4 -4
- package/cli/commands/list.ts +3 -9
- package/cli/commands/menu/backup-handlers.ts +12 -3
- package/cli/commands/menu/container-handlers.ts +65 -81
- package/cli/commands/menu/engine-handlers.ts +34 -16
- package/cli/commands/menu/index.ts +12 -12
- package/cli/commands/menu/shell-handlers.ts +27 -1
- package/cli/commands/menu/sql-handlers.ts +1 -0
- package/cli/constants.ts +38 -36
- package/cli/helpers.ts +72 -0
- package/cli/ui/prompts.ts +112 -1
- package/cli/ui/theme.ts +0 -2
- package/config/backup-formats.ts +14 -0
- package/config/engine-defaults.ts +13 -0
- package/config/engines.json +16 -0
- package/core/config-manager.ts +10 -0
- package/core/container-manager.ts +8 -6
- package/core/dependency-manager.ts +2 -0
- package/engines/index.ts +4 -0
- package/engines/mariadb/restore.ts +133 -57
- package/engines/mysql/restore.ts +160 -60
- package/engines/questdb/backup.ts +217 -0
- package/engines/questdb/binary-manager.ts +303 -0
- package/engines/questdb/binary-urls.ts +34 -0
- package/engines/questdb/hostdb-releases.ts +101 -0
- package/engines/questdb/index.ts +871 -0
- package/engines/questdb/restore.ts +235 -0
- package/engines/questdb/version-maps.ts +37 -0
- package/engines/questdb/version-validator.ts +121 -0
- package/package.json +3 -1
- package/types/index.ts +9 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuestDB Restore Implementation
|
|
3
|
+
*
|
|
4
|
+
* Restores SQL backups to QuestDB using PostgreSQL wire protocol.
|
|
5
|
+
* QuestDB is compatible with psql for executing SQL statements.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { open, readFile } from 'fs/promises'
|
|
9
|
+
import { spawn, spawnSync } from 'child_process'
|
|
10
|
+
import { configManager } from '../../core/config-manager'
|
|
11
|
+
import { logDebug } from '../../core/error-handler'
|
|
12
|
+
import type { BackupFormat, RestoreResult } from '../../types'
|
|
13
|
+
|
|
14
|
+
// Read only the first 8KB for format detection
|
|
15
|
+
const HEADER_SIZE = 8192
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect the backup format from file content
|
|
19
|
+
*/
|
|
20
|
+
export async function detectBackupFormat(filePath: string): Promise<BackupFormat> {
|
|
21
|
+
// Check extension first
|
|
22
|
+
const lowerPath = filePath.toLowerCase()
|
|
23
|
+
if (lowerPath.endsWith('.sql')) {
|
|
24
|
+
return {
|
|
25
|
+
format: 'sql',
|
|
26
|
+
description: 'SQL dump file',
|
|
27
|
+
restoreCommand: 'psql',
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Read file header for content-based detection
|
|
32
|
+
const buffer = Buffer.alloc(HEADER_SIZE)
|
|
33
|
+
const fd = await open(filePath, 'r')
|
|
34
|
+
let bytesRead: number
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const result = await fd.read(buffer, 0, HEADER_SIZE, 0)
|
|
38
|
+
bytesRead = result.bytesRead
|
|
39
|
+
} finally {
|
|
40
|
+
await fd.close()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const content = buffer.toString('utf-8', 0, bytesRead)
|
|
44
|
+
const lines = content.split(/\r?\n/)
|
|
45
|
+
|
|
46
|
+
// Check for SQL patterns
|
|
47
|
+
for (const line of lines.slice(0, 20)) {
|
|
48
|
+
const trimmed = line.trim().toUpperCase()
|
|
49
|
+
if (
|
|
50
|
+
trimmed.startsWith('CREATE TABLE') ||
|
|
51
|
+
trimmed.startsWith('INSERT INTO') ||
|
|
52
|
+
trimmed.startsWith('-- QUESTDB') ||
|
|
53
|
+
trimmed.startsWith('-- TABLE:')
|
|
54
|
+
) {
|
|
55
|
+
return {
|
|
56
|
+
format: 'sql',
|
|
57
|
+
description: 'SQL dump file',
|
|
58
|
+
restoreCommand: 'psql',
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
format: 'unknown',
|
|
65
|
+
description: 'Unknown format',
|
|
66
|
+
restoreCommand: '',
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse a QuestDB/PostgreSQL connection string
|
|
72
|
+
*/
|
|
73
|
+
export function parseConnectionString(connectionString: string): {
|
|
74
|
+
host: string
|
|
75
|
+
port: number
|
|
76
|
+
database: string
|
|
77
|
+
user: string
|
|
78
|
+
password?: string
|
|
79
|
+
} {
|
|
80
|
+
// Support both postgresql:// and questdb:// schemes
|
|
81
|
+
let url: URL
|
|
82
|
+
try {
|
|
83
|
+
// Replace questdb:// with postgresql:// for URL parsing
|
|
84
|
+
const normalized = connectionString.replace(/^questdb:\/\//, 'postgresql://')
|
|
85
|
+
url = new URL(normalized)
|
|
86
|
+
} catch {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid connection string: ${connectionString}\n` +
|
|
89
|
+
'Expected format: postgresql://user:password@host:port/database',
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
host: url.hostname || '127.0.0.1',
|
|
95
|
+
port: url.port ? parseInt(url.port, 10) : 8812,
|
|
96
|
+
database: url.pathname.replace(/^\//, '') || 'qdb',
|
|
97
|
+
user: url.username || 'admin',
|
|
98
|
+
password: url.password || 'quest',
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type RestoreOptions = {
|
|
103
|
+
containerName: string
|
|
104
|
+
port: number
|
|
105
|
+
database: string
|
|
106
|
+
version: string
|
|
107
|
+
clean?: boolean
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Restore a backup to QuestDB
|
|
112
|
+
*/
|
|
113
|
+
export async function restoreBackup(
|
|
114
|
+
backupPath: string,
|
|
115
|
+
options: RestoreOptions,
|
|
116
|
+
): Promise<RestoreResult> {
|
|
117
|
+
const { port, database, clean } = options
|
|
118
|
+
|
|
119
|
+
// Detect backup format
|
|
120
|
+
const format = await detectBackupFormat(backupPath)
|
|
121
|
+
if (format.format === 'unknown') {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Cannot detect backup format for: ${backupPath}\n` +
|
|
124
|
+
'Supported formats: .sql (SQL dump)',
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
logDebug(`Restoring ${format.format} backup to QuestDB database ${database}`)
|
|
129
|
+
|
|
130
|
+
// Find psql binary
|
|
131
|
+
let psqlPath = await configManager.getBinaryPath('psql')
|
|
132
|
+
if (!psqlPath) {
|
|
133
|
+
psqlPath = 'psql'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Build restore command args
|
|
137
|
+
// Use ON_ERROR_STOP to fail fast on any SQL error (otherwise psql continues silently)
|
|
138
|
+
const args = [
|
|
139
|
+
'-h', '127.0.0.1',
|
|
140
|
+
'-p', String(port),
|
|
141
|
+
'-U', 'admin',
|
|
142
|
+
'-d', database,
|
|
143
|
+
'-v', 'ON_ERROR_STOP=1',
|
|
144
|
+
'-f', backupPath,
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
// For clean restore, drop existing tables before restoring
|
|
148
|
+
if (clean) {
|
|
149
|
+
logDebug('Clean restore requested - extracting table names from backup')
|
|
150
|
+
|
|
151
|
+
// Read the SQL file and extract table names from CREATE TABLE statements
|
|
152
|
+
const sqlContent = await readFile(backupPath, 'utf-8')
|
|
153
|
+
// Match CREATE TABLE [IF NOT EXISTS] "table_name" or CREATE TABLE [IF NOT EXISTS] table_name
|
|
154
|
+
const tableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|(\w+))/gi
|
|
155
|
+
const tables: string[] = []
|
|
156
|
+
let match
|
|
157
|
+
|
|
158
|
+
while ((match = tableRegex.exec(sqlContent)) !== null) {
|
|
159
|
+
const tableName = match[1] || match[2]
|
|
160
|
+
if (tableName && !tables.includes(tableName)) {
|
|
161
|
+
tables.push(tableName)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (tables.length > 0) {
|
|
166
|
+
logDebug(`Found ${tables.length} tables to drop: ${tables.join(', ')}`)
|
|
167
|
+
|
|
168
|
+
// Execute DROP TABLE IF EXISTS for each table
|
|
169
|
+
for (const table of tables) {
|
|
170
|
+
const dropQuery = `DROP TABLE IF EXISTS "${table}";`
|
|
171
|
+
logDebug(`Executing: ${dropQuery}`)
|
|
172
|
+
|
|
173
|
+
const dropResult = spawnSync(psqlPath!, [
|
|
174
|
+
'-h', '127.0.0.1',
|
|
175
|
+
'-p', String(port),
|
|
176
|
+
'-U', 'admin',
|
|
177
|
+
'-d', database,
|
|
178
|
+
'-c', dropQuery,
|
|
179
|
+
], {
|
|
180
|
+
env: { ...process.env, PGPASSWORD: 'quest' },
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
if (dropResult.error) {
|
|
184
|
+
logDebug(`Warning: Failed to drop table ${table}: ${dropResult.error.message}`)
|
|
185
|
+
} else if (dropResult.status !== 0) {
|
|
186
|
+
logDebug(`Warning: DROP TABLE ${table} exited with code ${dropResult.status}`)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
logDebug('No CREATE TABLE statements found in backup')
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const proc = spawn(psqlPath!, args, {
|
|
196
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
197
|
+
env: { ...process.env, PGPASSWORD: 'quest' },
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
let stdout = ''
|
|
201
|
+
let stderr = ''
|
|
202
|
+
|
|
203
|
+
proc.stdout.on('data', (data: Buffer) => {
|
|
204
|
+
stdout += data.toString()
|
|
205
|
+
})
|
|
206
|
+
proc.stderr.on('data', (data: Buffer) => {
|
|
207
|
+
stderr += data.toString()
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
proc.on('close', (code) => {
|
|
211
|
+
if (code === 0) {
|
|
212
|
+
resolve({
|
|
213
|
+
format: format.format,
|
|
214
|
+
stdout,
|
|
215
|
+
stderr,
|
|
216
|
+
code: 0,
|
|
217
|
+
})
|
|
218
|
+
} else if (stderr.includes('already exists')) {
|
|
219
|
+
// Treat "already exists" as non-fatal (table recreation during restore)
|
|
220
|
+
resolve({
|
|
221
|
+
format: format.format,
|
|
222
|
+
stdout,
|
|
223
|
+
stderr,
|
|
224
|
+
code: code ?? 1,
|
|
225
|
+
})
|
|
226
|
+
} else {
|
|
227
|
+
reject(new Error(`Restore failed: ${stderr || `exit code ${code}`}`))
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
proc.on('error', (err) => {
|
|
232
|
+
reject(new Error(`Failed to execute psql: ${err.message}`))
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuestDB Version Maps
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: Keep this in sync with hostdb releases.json:
|
|
5
|
+
* https://github.com/robertjbass/hostdb/blob/main/releases.json
|
|
6
|
+
*
|
|
7
|
+
* QuestDB uses standard semantic versioning (e.g., 9.2.3)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const QUESTDB_VERSION_MAP: Record<string, string> = {
|
|
11
|
+
'9': '9.2.3',
|
|
12
|
+
'9.2': '9.2.3',
|
|
13
|
+
'9.2.3': '9.2.3',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SUPPORTED_MAJOR_VERSIONS = ['9']
|
|
17
|
+
export const FALLBACK_VERSION_MAP = QUESTDB_VERSION_MAP
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Normalize a version string to a full version
|
|
21
|
+
* e.g., '9' -> '9.2.3'
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeVersion(version: string): string {
|
|
24
|
+
// If already a full version, return as-is
|
|
25
|
+
if (/^\d+\.\d+\.\d+$/.test(version)) {
|
|
26
|
+
return version
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Try to look up in version map
|
|
30
|
+
const fullVersion = QUESTDB_VERSION_MAP[version]
|
|
31
|
+
if (fullVersion) {
|
|
32
|
+
return fullVersion
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Return as-is if not found
|
|
36
|
+
return version
|
|
37
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuestDB Version Validator
|
|
3
|
+
*
|
|
4
|
+
* Provides version parsing, validation, and comparison utilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { SUPPORTED_MAJOR_VERSIONS, QUESTDB_VERSION_MAP } from './version-maps'
|
|
8
|
+
|
|
9
|
+
export type ParsedVersion = {
|
|
10
|
+
major: number
|
|
11
|
+
minor: number
|
|
12
|
+
patch: number
|
|
13
|
+
full: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse a version string into components
|
|
18
|
+
* @param version Version string (e.g., '9.2.3', '9.2', '9')
|
|
19
|
+
* @returns Parsed version or null if invalid
|
|
20
|
+
*/
|
|
21
|
+
export function parseVersion(version: string): ParsedVersion | null {
|
|
22
|
+
// Match version patterns: 9.2.3, 9.2, 9
|
|
23
|
+
const match = version.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?$/)
|
|
24
|
+
if (!match) return null
|
|
25
|
+
|
|
26
|
+
const major = parseInt(match[1], 10)
|
|
27
|
+
const minor = match[2] ? parseInt(match[2], 10) : 0
|
|
28
|
+
const patch = match[3] ? parseInt(match[3], 10) : 0
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
major,
|
|
32
|
+
minor,
|
|
33
|
+
patch,
|
|
34
|
+
full: `${major}.${minor}.${patch}`,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if a version is supported
|
|
40
|
+
*/
|
|
41
|
+
export function isVersionSupported(version: string): boolean {
|
|
42
|
+
const parsed = parseVersion(version)
|
|
43
|
+
if (!parsed) return false
|
|
44
|
+
|
|
45
|
+
const majorStr = String(parsed.major)
|
|
46
|
+
return SUPPORTED_MAJOR_VERSIONS.includes(majorStr)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the major version string from a version
|
|
51
|
+
*/
|
|
52
|
+
export function getMajorVersion(version: string): string | null {
|
|
53
|
+
const parsed = parseVersion(version)
|
|
54
|
+
if (!parsed) return null
|
|
55
|
+
return String(parsed.major)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Compare two versions
|
|
60
|
+
* @returns -1 if a < b, 0 if a == b, 1 if a > b
|
|
61
|
+
*/
|
|
62
|
+
export function compareVersions(a: string, b: string): number {
|
|
63
|
+
const parsedA = parseVersion(a)
|
|
64
|
+
const parsedB = parseVersion(b)
|
|
65
|
+
|
|
66
|
+
if (!parsedA || !parsedB) {
|
|
67
|
+
// Fall back to string comparison if parsing fails
|
|
68
|
+
return a.localeCompare(b)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (parsedA.major !== parsedB.major) {
|
|
72
|
+
return parsedA.major - parsedB.major
|
|
73
|
+
}
|
|
74
|
+
if (parsedA.minor !== parsedB.minor) {
|
|
75
|
+
return parsedA.minor - parsedB.minor
|
|
76
|
+
}
|
|
77
|
+
return parsedA.patch - parsedB.patch
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if two versions are compatible for backup/restore
|
|
82
|
+
* QuestDB allows restoring to same or newer major version
|
|
83
|
+
*/
|
|
84
|
+
export function isVersionCompatible(
|
|
85
|
+
sourceVersion: string,
|
|
86
|
+
targetVersion: string,
|
|
87
|
+
): boolean {
|
|
88
|
+
const sourceMajor = getMajorVersion(sourceVersion)
|
|
89
|
+
const targetMajor = getMajorVersion(targetVersion)
|
|
90
|
+
|
|
91
|
+
if (!sourceMajor || !targetMajor) return false
|
|
92
|
+
|
|
93
|
+
// Same major version is always compatible
|
|
94
|
+
if (sourceMajor === targetMajor) return true
|
|
95
|
+
|
|
96
|
+
// Target major must be >= source major
|
|
97
|
+
return parseInt(targetMajor, 10) >= parseInt(sourceMajor, 10)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve a version alias to full version
|
|
102
|
+
*/
|
|
103
|
+
export function resolveVersion(version: string): string {
|
|
104
|
+
// Check if it's already a full version in the map
|
|
105
|
+
if (QUESTDB_VERSION_MAP[version]) {
|
|
106
|
+
return QUESTDB_VERSION_MAP[version]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// If already a full version format, return as-is
|
|
110
|
+
if (/^\d+\.\d+\.\d+$/.test(version)) {
|
|
111
|
+
return version
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Try major version lookup
|
|
115
|
+
const majorVersion = getMajorVersion(version)
|
|
116
|
+
if (majorVersion && QUESTDB_VERSION_MAP[majorVersion]) {
|
|
117
|
+
return QUESTDB_VERSION_MAP[majorVersion]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return version
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spindb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.3",
|
|
4
4
|
"description": "Zero-config Docker-free local database containers. Create, backup, and clone a variety of popular databases.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"chalk": "^5.3.0",
|
|
72
72
|
"commander": "^12.1.0",
|
|
73
73
|
"inquirer": "^9.3.7",
|
|
74
|
+
"inquirer-autocomplete-prompt": "^3.0.1",
|
|
74
75
|
"ora": "^8.1.1",
|
|
75
76
|
"string-width": "^8.1.0",
|
|
76
77
|
"tsx": "^4.7.0",
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
"devDependencies": {
|
|
80
81
|
"@eslint/js": "^9.39.1",
|
|
81
82
|
"@types/inquirer": "^9.0.7",
|
|
83
|
+
"@types/inquirer-autocomplete-prompt": "^3.0.3",
|
|
82
84
|
"@types/node": "^20.10.0",
|
|
83
85
|
"@types/unzipper": "^0.10.11",
|
|
84
86
|
"eslint": "^9.39.1",
|
package/types/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export enum Engine {
|
|
|
36
36
|
CouchDB = 'couchdb',
|
|
37
37
|
CockroachDB = 'cockroachdb',
|
|
38
38
|
SurrealDB = 'surrealdb',
|
|
39
|
+
QuestDB = 'questdb',
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// Supported operating systems (matches Node.js process.platform)
|
|
@@ -71,6 +72,7 @@ export const ALL_ENGINES = [
|
|
|
71
72
|
Engine.CouchDB,
|
|
72
73
|
Engine.CockroachDB,
|
|
73
74
|
Engine.SurrealDB,
|
|
75
|
+
Engine.QuestDB,
|
|
74
76
|
] as const
|
|
75
77
|
|
|
76
78
|
// File-based engines (no server process, data stored in user project directories)
|
|
@@ -199,6 +201,7 @@ export type FerretDBFormat = 'sql' | 'custom'
|
|
|
199
201
|
export type CouchDBFormat = 'json'
|
|
200
202
|
export type CockroachDBFormat = 'sql'
|
|
201
203
|
export type SurrealDBFormat = 'surql'
|
|
204
|
+
export type QuestDBFormat = 'sql'
|
|
202
205
|
|
|
203
206
|
// Union of all backup formats
|
|
204
207
|
export type BackupFormatType =
|
|
@@ -217,6 +220,7 @@ export type BackupFormatType =
|
|
|
217
220
|
| CouchDBFormat
|
|
218
221
|
| CockroachDBFormat
|
|
219
222
|
| SurrealDBFormat
|
|
223
|
+
| QuestDBFormat
|
|
220
224
|
|
|
221
225
|
// Mapping from Engine to its corresponding backup format type
|
|
222
226
|
type EngineFormatMap = {
|
|
@@ -235,6 +239,7 @@ type EngineFormatMap = {
|
|
|
235
239
|
[Engine.CouchDB]: CouchDBFormat
|
|
236
240
|
[Engine.CockroachDB]: CockroachDBFormat
|
|
237
241
|
[Engine.SurrealDB]: SurrealDBFormat
|
|
242
|
+
[Engine.QuestDB]: QuestDBFormat
|
|
238
243
|
}
|
|
239
244
|
|
|
240
245
|
// Helper type to get format type for a specific engine
|
|
@@ -344,6 +349,8 @@ export type BinaryTool =
|
|
|
344
349
|
| 'cockroach'
|
|
345
350
|
// SurrealDB tools
|
|
346
351
|
| 'surreal'
|
|
352
|
+
// QuestDB tools
|
|
353
|
+
| 'questdb'
|
|
347
354
|
// Enhanced shells (optional)
|
|
348
355
|
| 'pgcli'
|
|
349
356
|
| 'mycli'
|
|
@@ -423,6 +430,8 @@ export type SpinDBConfig = {
|
|
|
423
430
|
cockroach?: BinaryConfig
|
|
424
431
|
// SurrealDB tools
|
|
425
432
|
surreal?: BinaryConfig
|
|
433
|
+
// QuestDB tools
|
|
434
|
+
questdb?: BinaryConfig
|
|
426
435
|
// Enhanced shells (optional)
|
|
427
436
|
pgcli?: BinaryConfig
|
|
428
437
|
mycli?: BinaryConfig
|