@bytebase/dbhub 1.3.1 → 1.5.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/{chunk-ABTVMHD6.js → chunk-55X2VY5D.js} +1 -1
- package/dist/chunk-5XSBOK3W.js +63 -0
- package/dist/{chunk-5FL4XMWG.js → chunk-PDKPK25P.js} +356 -106
- package/dist/chunk-SJ5BRPQA.js +500 -0
- package/dist/{chunk-YIGJHEU6.js → chunk-X5OCFRNL.js} +170 -27
- package/dist/index.js +116 -32
- package/dist/mariadb-RWOMZTA6.js +270 -0
- package/dist/mysql-ZXO4TWLG.js +312 -0
- package/dist/{oracle-OUKBU52X.js → oracle-UAVN3F4U.js} +2 -2
- package/dist/{postgres-6XJ7V4J4.js → postgres-KCFNDQLG.js} +179 -29
- package/dist/{registry-ZTHDPLKP.js → registry-ZIVIYCB2.js} +2 -1
- package/dist/{sqlite-AO2BKIC6.js → sqlite-WKY6543Q.js} +2 -2
- package/dist/{sqlserver-AJUK6L55.js → sqlserver-25MAE77Q.js} +312 -372
- package/package.json +1 -1
- package/dist/chunk-6SZKW6GD.js +0 -179
- package/dist/mariadb-L7Z55ROY.js +0 -551
- package/dist/mysql-XXIHWQGR.js +0 -601
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isClientSideTimeout
|
|
3
|
+
} from "./chunk-5XSBOK3W.js";
|
|
4
|
+
import {
|
|
5
|
+
computeHitRatioPct,
|
|
6
|
+
toNullableNumber
|
|
7
|
+
} from "./chunk-FU2ZJE4E.js";
|
|
8
|
+
import {
|
|
9
|
+
quoteIdentifier
|
|
10
|
+
} from "./chunk-X2EC47W4.js";
|
|
11
|
+
import {
|
|
12
|
+
obfuscateDSNPassword
|
|
13
|
+
} from "./chunk-IXC4NQGC.js";
|
|
14
|
+
|
|
15
|
+
// src/connectors/mysql-family-health-check.ts
|
|
16
|
+
async function getMySQLFamilyHealthCheck(query) {
|
|
17
|
+
const [processlistRows, maxConnRows, bufferPoolRows, trxOutcome] = await Promise.all([
|
|
18
|
+
query(`
|
|
19
|
+
SELECT
|
|
20
|
+
COUNT(*) AS total,
|
|
21
|
+
SUM(CASE WHEN COMMAND != 'Sleep' THEN 1 ELSE 0 END) AS active,
|
|
22
|
+
SUM(CASE WHEN COMMAND = 'Sleep' THEN 1 ELSE 0 END) AS idle,
|
|
23
|
+
MAX(CASE WHEN COMMAND != 'Sleep' THEN TIME ELSE NULL END) AS longest_active_query_seconds
|
|
24
|
+
FROM information_schema.PROCESSLIST
|
|
25
|
+
WHERE ID <> CONNECTION_ID()
|
|
26
|
+
`),
|
|
27
|
+
query(`SHOW VARIABLES LIKE 'max_connections'`),
|
|
28
|
+
query(`
|
|
29
|
+
SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')
|
|
30
|
+
`),
|
|
31
|
+
// Joining PROCESSLIST against INNODB_TRX to detect idle-in-transaction
|
|
32
|
+
// sessions requires the PROCESS privilege - unlike a plain PROCESSLIST
|
|
33
|
+
// select, which silently narrows to the caller's own session instead of
|
|
34
|
+
// erroring. Run concurrently with the other queries above, but resolve
|
|
35
|
+
// rather than reject on failure so it degrades instead of failing the
|
|
36
|
+
// whole health check.
|
|
37
|
+
query(`
|
|
38
|
+
SELECT
|
|
39
|
+
COUNT(*) AS idle_in_transaction,
|
|
40
|
+
MAX(TIMESTAMPDIFF(SECOND, t.trx_started, NOW())) AS longest_idle_in_transaction_seconds
|
|
41
|
+
FROM information_schema.INNODB_TRX t
|
|
42
|
+
JOIN information_schema.PROCESSLIST p ON p.ID = t.trx_mysql_thread_id
|
|
43
|
+
WHERE p.COMMAND = 'Sleep'
|
|
44
|
+
`).then(
|
|
45
|
+
(rows) => ({ ok: true, rows }),
|
|
46
|
+
() => ({ ok: false })
|
|
47
|
+
)
|
|
48
|
+
]);
|
|
49
|
+
const notes = [];
|
|
50
|
+
let idleInTransaction = 0;
|
|
51
|
+
let longestIdleInTransactionSeconds = null;
|
|
52
|
+
if (trxOutcome.ok) {
|
|
53
|
+
idleInTransaction = Number(trxOutcome.rows[0].idle_in_transaction);
|
|
54
|
+
longestIdleInTransactionSeconds = toNullableNumber(trxOutcome.rows[0].longest_idle_in_transaction_seconds);
|
|
55
|
+
} else {
|
|
56
|
+
notes.push(
|
|
57
|
+
"Connected user lacks the PROCESS privilege: PROCESSLIST visibility is restricted to the connecting user's own sessions, and this diagnostic session is excluded from the count, so connection counts and active-query duration may under-report (down to 0). Idle-in-transaction detection is unavailable."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const proc = processlistRows[0];
|
|
61
|
+
const bufferPoolStats = Object.fromEntries(bufferPoolRows.map((row) => [row.Variable_name, row.Value]));
|
|
62
|
+
const readRequests = Number(bufferPoolStats.Innodb_buffer_pool_read_requests ?? 0);
|
|
63
|
+
const reads = Number(bufferPoolStats.Innodb_buffer_pool_reads ?? 0);
|
|
64
|
+
return {
|
|
65
|
+
connections: {
|
|
66
|
+
total: Number(proc.total),
|
|
67
|
+
active: Number(proc.active),
|
|
68
|
+
idle: Number(proc.idle),
|
|
69
|
+
idleInTransaction,
|
|
70
|
+
// MySQL/MariaDB's InnoDB has no equivalent of Postgres's "idle in
|
|
71
|
+
// transaction (aborted)" state - a failed statement doesn't leave the
|
|
72
|
+
// session's transaction in a distinct aborted-but-open state the way
|
|
73
|
+
// Postgres does - so idleInTransactionAborted is left undefined.
|
|
74
|
+
maxConnections: maxConnRows.length > 0 ? Number(maxConnRows[0].Value) : null,
|
|
75
|
+
longestIdleInTransactionSeconds,
|
|
76
|
+
longestActiveQuerySeconds: toNullableNumber(proc.longest_active_query_seconds)
|
|
77
|
+
},
|
|
78
|
+
bufferCache: {
|
|
79
|
+
hitRatioPct: computeHitRatioPct(readRequests, reads),
|
|
80
|
+
blocksHit: readRequests - reads,
|
|
81
|
+
blocksRead: reads
|
|
82
|
+
},
|
|
83
|
+
...notes.length > 0 ? { notes } : {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/connectors/mysql-family-introspection.ts
|
|
88
|
+
async function getSchemas(query) {
|
|
89
|
+
try {
|
|
90
|
+
const rows = await query(`
|
|
91
|
+
SELECT SCHEMA_NAME
|
|
92
|
+
FROM INFORMATION_SCHEMA.SCHEMATA
|
|
93
|
+
WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
|
|
94
|
+
ORDER BY SCHEMA_NAME
|
|
95
|
+
`);
|
|
96
|
+
return rows.map((row) => row.SCHEMA_NAME);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Error getting schemas:", error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function getTables(query, schema) {
|
|
103
|
+
try {
|
|
104
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
105
|
+
const queryParams = schema ? [schema] : [];
|
|
106
|
+
const rows = await query(
|
|
107
|
+
`
|
|
108
|
+
SELECT TABLE_NAME
|
|
109
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
110
|
+
${schemaClause}
|
|
111
|
+
AND TABLE_TYPE = 'BASE TABLE'
|
|
112
|
+
ORDER BY TABLE_NAME
|
|
113
|
+
`,
|
|
114
|
+
queryParams
|
|
115
|
+
);
|
|
116
|
+
return rows.map((row) => row.TABLE_NAME);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("Error getting tables:", error);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function getViews(query, schema) {
|
|
123
|
+
try {
|
|
124
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
125
|
+
const queryParams = schema ? [schema] : [];
|
|
126
|
+
const rows = await query(
|
|
127
|
+
`
|
|
128
|
+
SELECT TABLE_NAME
|
|
129
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
130
|
+
${schemaClause}
|
|
131
|
+
AND TABLE_TYPE = 'VIEW'
|
|
132
|
+
ORDER BY TABLE_NAME
|
|
133
|
+
`,
|
|
134
|
+
queryParams
|
|
135
|
+
);
|
|
136
|
+
return rows.map((row) => row.TABLE_NAME);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error("Error getting views:", error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function tableExists(query, tableName, schema) {
|
|
143
|
+
try {
|
|
144
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
145
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
146
|
+
const rows = await query(
|
|
147
|
+
`
|
|
148
|
+
SELECT COUNT(*) AS COUNT
|
|
149
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
150
|
+
${schemaClause}
|
|
151
|
+
AND TABLE_NAME = ?
|
|
152
|
+
`,
|
|
153
|
+
queryParams
|
|
154
|
+
);
|
|
155
|
+
return rows[0].COUNT > 0;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error("Error checking if table exists:", error);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function getTableIndexes(query, tableName, schema) {
|
|
162
|
+
try {
|
|
163
|
+
const schemaClause = schema ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
|
|
164
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
165
|
+
const indexRows = await query(
|
|
166
|
+
`
|
|
167
|
+
SELECT
|
|
168
|
+
INDEX_NAME,
|
|
169
|
+
COLUMN_NAME,
|
|
170
|
+
NON_UNIQUE,
|
|
171
|
+
SEQ_IN_INDEX
|
|
172
|
+
FROM
|
|
173
|
+
INFORMATION_SCHEMA.STATISTICS
|
|
174
|
+
WHERE
|
|
175
|
+
${schemaClause}
|
|
176
|
+
AND TABLE_NAME = ?
|
|
177
|
+
ORDER BY
|
|
178
|
+
INDEX_NAME,
|
|
179
|
+
SEQ_IN_INDEX
|
|
180
|
+
`,
|
|
181
|
+
queryParams
|
|
182
|
+
);
|
|
183
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
184
|
+
for (const row of indexRows) {
|
|
185
|
+
const indexName = row.INDEX_NAME;
|
|
186
|
+
const columnName = row.COLUMN_NAME;
|
|
187
|
+
const isUnique = row.NON_UNIQUE === 0;
|
|
188
|
+
const isPrimary = indexName === "PRIMARY";
|
|
189
|
+
if (!indexMap.has(indexName)) {
|
|
190
|
+
indexMap.set(indexName, {
|
|
191
|
+
columns: [],
|
|
192
|
+
is_unique: isUnique,
|
|
193
|
+
is_primary: isPrimary
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const indexInfo = indexMap.get(indexName);
|
|
197
|
+
indexInfo.columns.push(columnName);
|
|
198
|
+
}
|
|
199
|
+
const results = [];
|
|
200
|
+
indexMap.forEach((indexInfo, indexName) => {
|
|
201
|
+
results.push({
|
|
202
|
+
index_name: indexName,
|
|
203
|
+
column_names: indexInfo.columns,
|
|
204
|
+
is_unique: indexInfo.is_unique,
|
|
205
|
+
is_primary: indexInfo.is_primary
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
return results;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error("Error getting table indexes:", error);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function getTableSchema(query, tableName, schema) {
|
|
215
|
+
try {
|
|
216
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
217
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
218
|
+
const rows = await query(
|
|
219
|
+
`
|
|
220
|
+
SELECT
|
|
221
|
+
COLUMN_NAME as column_name,
|
|
222
|
+
DATA_TYPE as data_type,
|
|
223
|
+
IS_NULLABLE as is_nullable,
|
|
224
|
+
COLUMN_DEFAULT as column_default,
|
|
225
|
+
COLUMN_COMMENT as description
|
|
226
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
227
|
+
${schemaClause}
|
|
228
|
+
AND TABLE_NAME = ?
|
|
229
|
+
ORDER BY ORDINAL_POSITION
|
|
230
|
+
`,
|
|
231
|
+
queryParams
|
|
232
|
+
);
|
|
233
|
+
return rows.map((row) => ({
|
|
234
|
+
...row,
|
|
235
|
+
description: row.description || null
|
|
236
|
+
}));
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error("Error getting table schema:", error);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function getTableComment(query, tableName, schema) {
|
|
243
|
+
try {
|
|
244
|
+
const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
|
|
245
|
+
const queryParams = schema ? [schema, tableName] : [tableName];
|
|
246
|
+
const rows = await query(
|
|
247
|
+
`
|
|
248
|
+
SELECT TABLE_COMMENT
|
|
249
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
250
|
+
${schemaClause}
|
|
251
|
+
AND TABLE_NAME = ?
|
|
252
|
+
`,
|
|
253
|
+
queryParams
|
|
254
|
+
);
|
|
255
|
+
if (rows.length > 0) {
|
|
256
|
+
return rows[0].TABLE_COMMENT || null;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async function getStoredProcedures(query, schema, routineType) {
|
|
264
|
+
try {
|
|
265
|
+
const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
|
|
266
|
+
const queryParams = schema ? [schema] : [];
|
|
267
|
+
let typeFilter = "";
|
|
268
|
+
if (routineType === "function") {
|
|
269
|
+
typeFilter = " AND ROUTINE_TYPE = 'FUNCTION'";
|
|
270
|
+
} else if (routineType === "procedure") {
|
|
271
|
+
typeFilter = " AND ROUTINE_TYPE = 'PROCEDURE'";
|
|
272
|
+
}
|
|
273
|
+
const rows = await query(
|
|
274
|
+
`
|
|
275
|
+
SELECT ROUTINE_NAME
|
|
276
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
277
|
+
${schemaClause}${typeFilter}
|
|
278
|
+
ORDER BY ROUTINE_NAME
|
|
279
|
+
`,
|
|
280
|
+
queryParams
|
|
281
|
+
);
|
|
282
|
+
return rows.map((row) => row.ROUTINE_NAME);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error("Error getting stored procedures:", error);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function getStoredProcedureDetail(query, procedureName, schema) {
|
|
289
|
+
try {
|
|
290
|
+
const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
|
|
291
|
+
const queryParams = schema ? [schema, procedureName] : [procedureName];
|
|
292
|
+
const rows = await query(
|
|
293
|
+
`
|
|
294
|
+
SELECT
|
|
295
|
+
r.ROUTINE_NAME AS procedure_name,
|
|
296
|
+
CASE
|
|
297
|
+
WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
|
|
298
|
+
ELSE 'function'
|
|
299
|
+
END AS procedure_type,
|
|
300
|
+
LOWER(r.ROUTINE_TYPE) AS routine_type,
|
|
301
|
+
r.ROUTINE_DEFINITION,
|
|
302
|
+
r.DTD_IDENTIFIER AS return_type,
|
|
303
|
+
(
|
|
304
|
+
SELECT GROUP_CONCAT(
|
|
305
|
+
CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
|
|
306
|
+
ORDER BY p.ORDINAL_POSITION
|
|
307
|
+
SEPARATOR ', '
|
|
308
|
+
)
|
|
309
|
+
FROM INFORMATION_SCHEMA.PARAMETERS p
|
|
310
|
+
WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
|
|
311
|
+
AND p.SPECIFIC_NAME = r.ROUTINE_NAME
|
|
312
|
+
AND p.PARAMETER_NAME IS NOT NULL
|
|
313
|
+
) AS parameter_list
|
|
314
|
+
FROM INFORMATION_SCHEMA.ROUTINES r
|
|
315
|
+
${schemaClause}
|
|
316
|
+
AND r.ROUTINE_NAME = ?
|
|
317
|
+
`,
|
|
318
|
+
queryParams
|
|
319
|
+
);
|
|
320
|
+
if (rows.length === 0) {
|
|
321
|
+
const schemaName = schema || "current schema";
|
|
322
|
+
throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
|
|
323
|
+
}
|
|
324
|
+
const procedure = rows[0];
|
|
325
|
+
let definition = procedure.ROUTINE_DEFINITION;
|
|
326
|
+
try {
|
|
327
|
+
const schemaValue = schema || await getCurrentSchema(query);
|
|
328
|
+
const quotedSchema = quoteIdentifier(schemaValue, "mysql");
|
|
329
|
+
const quotedProcName = quoteIdentifier(procedureName, "mysql");
|
|
330
|
+
if (procedure.procedure_type === "procedure") {
|
|
331
|
+
try {
|
|
332
|
+
const defRows = await query(`
|
|
333
|
+
SHOW CREATE PROCEDURE ${quotedSchema}.${quotedProcName}
|
|
334
|
+
`);
|
|
335
|
+
if (defRows && defRows.length > 0) {
|
|
336
|
+
definition = defRows[0]["Create Procedure"];
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
console.error(`Error getting procedure definition with SHOW CREATE: ${err}`);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
try {
|
|
343
|
+
const defRows = await query(`
|
|
344
|
+
SHOW CREATE FUNCTION ${quotedSchema}.${quotedProcName}
|
|
345
|
+
`);
|
|
346
|
+
if (defRows && defRows.length > 0) {
|
|
347
|
+
definition = defRows[0]["Create Function"];
|
|
348
|
+
}
|
|
349
|
+
} catch (innerErr) {
|
|
350
|
+
console.error(`Error getting function definition with SHOW CREATE: ${innerErr}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!definition) {
|
|
354
|
+
const bodyRows = await query(
|
|
355
|
+
`
|
|
356
|
+
SELECT ROUTINE_DEFINITION, ROUTINE_BODY
|
|
357
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
358
|
+
WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
|
|
359
|
+
`,
|
|
360
|
+
[schemaValue, procedureName]
|
|
361
|
+
);
|
|
362
|
+
if (bodyRows && bodyRows.length > 0) {
|
|
363
|
+
if (bodyRows[0].ROUTINE_DEFINITION) {
|
|
364
|
+
definition = bodyRows[0].ROUTINE_DEFINITION;
|
|
365
|
+
} else if (bodyRows[0].ROUTINE_BODY) {
|
|
366
|
+
definition = bodyRows[0].ROUTINE_BODY;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error(`Error getting procedure/function details: ${error}`);
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
procedure_name: procedure.procedure_name,
|
|
375
|
+
procedure_type: procedure.procedure_type,
|
|
376
|
+
language: "sql",
|
|
377
|
+
// MySQL/MariaDB procedures are generally in SQL
|
|
378
|
+
parameter_list: procedure.parameter_list || "",
|
|
379
|
+
return_type: procedure.routine_type === "function" ? procedure.return_type : void 0,
|
|
380
|
+
definition: definition || void 0
|
|
381
|
+
};
|
|
382
|
+
} catch (error) {
|
|
383
|
+
console.error("Error getting stored procedure detail:", error);
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function getCurrentSchema(query) {
|
|
388
|
+
const rows = await query("SELECT DATABASE() AS DB");
|
|
389
|
+
return rows[0].DB;
|
|
390
|
+
}
|
|
391
|
+
async function getDefaultSchema(query) {
|
|
392
|
+
const rows = await query("SELECT DATABASE() AS DB");
|
|
393
|
+
return rows[0]?.DB ?? null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/utils/dsn-database.ts
|
|
397
|
+
var MissingDatabaseError = class extends Error {
|
|
398
|
+
constructor(message) {
|
|
399
|
+
super(message);
|
|
400
|
+
this.name = "MissingDatabaseError";
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
function requireDatabaseInDSN(database, dsn, label) {
|
|
404
|
+
if (database) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
throw new MissingDatabaseError(
|
|
408
|
+
`${label} DSN must name a database.
|
|
409
|
+
Provided: ${obfuscateDSNPassword(dsn)}
|
|
410
|
+
Add the database to the DSN, e.g. ...:3306/mydb
|
|
411
|
+
To work with several databases, define one [[sources]] entry per database in a TOML config file: https://dbhub.ai/config/toml`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/utils/multi-statement-result-parser.ts
|
|
416
|
+
function isMetadataObject(element) {
|
|
417
|
+
if (!element || typeof element !== "object" || Array.isArray(element)) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return "affectedRows" in element || "insertId" in element || "fieldCount" in element || "warningStatus" in element;
|
|
421
|
+
}
|
|
422
|
+
function isMultiStatementResult(results) {
|
|
423
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
const firstElement = results[0];
|
|
427
|
+
return isMetadataObject(firstElement) || Array.isArray(firstElement);
|
|
428
|
+
}
|
|
429
|
+
function buildResultSetsFromMultiStatement(results, statementTexts) {
|
|
430
|
+
const sql = statementTexts?.length === results.length ? statementTexts : void 0;
|
|
431
|
+
return results.map((result, index) => {
|
|
432
|
+
if (Array.isArray(result)) {
|
|
433
|
+
return { sql: sql?.[index], rows: result, rowCount: result.length };
|
|
434
|
+
}
|
|
435
|
+
if (isMetadataObject(result)) {
|
|
436
|
+
return { sql: sql?.[index], rows: [], rowCount: result.affectedRows || 0 };
|
|
437
|
+
}
|
|
438
|
+
return { sql: sql?.[index], rows: [], rowCount: 0 };
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function parseQueryResultSets(results, statementTexts) {
|
|
442
|
+
const singleStatementSql = statementTexts?.length === 1 ? statementTexts[0] : void 0;
|
|
443
|
+
if (isMetadataObject(results)) {
|
|
444
|
+
return [{ sql: singleStatementSql, rows: [], rowCount: results.affectedRows || 0 }];
|
|
445
|
+
}
|
|
446
|
+
if (!Array.isArray(results)) {
|
|
447
|
+
return [{ sql: singleStatementSql, rows: [], rowCount: 0 }];
|
|
448
|
+
}
|
|
449
|
+
if (isMultiStatementResult(results)) {
|
|
450
|
+
return buildResultSetsFromMultiStatement(results, statementTexts);
|
|
451
|
+
}
|
|
452
|
+
return [{ sql: singleStatementSql, rows: results, rowCount: results.length }];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/utils/readonly-transaction.ts
|
|
456
|
+
async function withReadOnlyTransaction(conn, readonly, supportsReadOnlyTransaction, execute) {
|
|
457
|
+
if (!readonly) {
|
|
458
|
+
return execute();
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
await conn.query(
|
|
462
|
+
supportsReadOnlyTransaction ? "START TRANSACTION READ ONLY" : "START TRANSACTION"
|
|
463
|
+
);
|
|
464
|
+
const result = await execute();
|
|
465
|
+
await conn.query(supportsReadOnlyTransaction ? "COMMIT" : "ROLLBACK");
|
|
466
|
+
return result;
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (!isClientSideTimeout(error)) {
|
|
469
|
+
try {
|
|
470
|
+
await conn.query("ROLLBACK");
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/utils/server-flavor.ts
|
|
479
|
+
function isTiDBVersion(version) {
|
|
480
|
+
return typeof version === "string" && /tidb/i.test(version);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export {
|
|
484
|
+
getMySQLFamilyHealthCheck,
|
|
485
|
+
getSchemas,
|
|
486
|
+
getTables,
|
|
487
|
+
getViews,
|
|
488
|
+
tableExists,
|
|
489
|
+
getTableIndexes,
|
|
490
|
+
getTableSchema,
|
|
491
|
+
getTableComment,
|
|
492
|
+
getStoredProcedures,
|
|
493
|
+
getStoredProcedureDetail,
|
|
494
|
+
getDefaultSchema,
|
|
495
|
+
MissingDatabaseError,
|
|
496
|
+
requireDatabaseInDSN,
|
|
497
|
+
parseQueryResultSets,
|
|
498
|
+
withReadOnlyTransaction,
|
|
499
|
+
isTiDBVersion
|
|
500
|
+
};
|