datapeek 0.1.8 → 0.1.10
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/chunk-3XB4P3G3.js +228 -0
- package/dist/cli/{chunk-5X2YZYYM.js → chunk-G7KSYREO.js} +43 -5
- package/dist/cli/chunk-Y3EHCZQX.js +220 -0
- package/dist/cli/{chunk-PTI2CUG6.js → chunk-Z2LV7EWL.js} +75 -4
- package/dist/cli/dev.js +317 -49
- package/dist/cli/index.js +317 -49
- package/dist/cli/{mssql-D7NDIUG7.js → mssql-AYS72PRQ.js} +1 -1
- package/dist/cli/{mssql-5VFXLOGV.js → mssql-HI3S4B7E.js} +3 -1
- package/dist/cli/{mssql-4KECNFPA.js → mssql-JB63TSGG.js} +4 -1
- package/dist/cli/mssql-Y5A62OYG.js +20 -0
- package/dist/client/assets/index-BhZ7NrhL.css +1 -0
- package/dist/client/assets/index-D-mash8_.js +370 -0
- package/dist/client/index.html +2 -2
- package/dist/server/{chunk-XFS5KTMI.js → chunk-JYZHE6GB.js} +43 -5
- package/dist/server/{chunk-7G2KHS5I.js → chunk-MJJGJZFC.js} +75 -4
- package/dist/server/chunk-VEX42Z2L.js +225 -0
- package/dist/server/chunk-VQPGGH2I.js +217 -0
- package/dist/server/dev.js +317 -49
- package/dist/server/index.js +317 -49
- package/dist/server/{mssql-UUT2IKOP.js → mssql-H6LKT5KN.js} +1 -1
- package/dist/server/mssql-JDRJT5H4.js +18 -0
- package/dist/server/mssql-LLTFKNX2.js +18 -0
- package/dist/server/mssql-UZUN227W.js +18 -0
- package/package.json +4 -3
- package/dist/cli/chunk-4IGBRCNN.js +0 -118
- package/dist/cli/chunk-IPR5JXB5.js +0 -108
- package/dist/cli/mssql-2REU4IX7.js +0 -17
- package/dist/client/assets/index-C32MC3if.js +0 -239
- package/dist/client/assets/index-piPKidzx.css +0 -1
- package/dist/server/chunk-XMPF5YCJ.js +0 -106
- package/dist/server/mssql-7KV6MCZK.js +0 -16
- package/dist/server/mssql-XRYTOVSF.js +0 -16
package/dist/server/dev.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
executeQueryMultiple,
|
|
6
6
|
getConnection,
|
|
7
7
|
testConnection
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VEX42Z2L.js";
|
|
9
9
|
|
|
10
10
|
// src/server/index.ts
|
|
11
11
|
import express from "express";
|
|
@@ -86,7 +86,7 @@ connectionRoutes.delete("/", async (req, res) => {
|
|
|
86
86
|
});
|
|
87
87
|
connectionRoutes.get("/status", async (req, res) => {
|
|
88
88
|
try {
|
|
89
|
-
const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-
|
|
89
|
+
const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-UZUN227W.js");
|
|
90
90
|
const pool = getConnection2();
|
|
91
91
|
if (pool && pool.connected) {
|
|
92
92
|
try {
|
|
@@ -96,7 +96,7 @@ connectionRoutes.get("/status", async (req, res) => {
|
|
|
96
96
|
} catch (error) {
|
|
97
97
|
const errorMessage = error.message || "";
|
|
98
98
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
99
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
99
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
100
100
|
await disconnect2();
|
|
101
101
|
}
|
|
102
102
|
res.json({ connected: false });
|
|
@@ -132,7 +132,7 @@ tableRoutes.get("/", async (req, res) => {
|
|
|
132
132
|
} catch (error) {
|
|
133
133
|
const errorMessage = error.message || "";
|
|
134
134
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
135
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
135
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
136
136
|
await disconnect2();
|
|
137
137
|
}
|
|
138
138
|
res.status(500).json({ error: error.message || "Failed to fetch tables" });
|
|
@@ -152,7 +152,10 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
152
152
|
c.CHARACTER_MAXIMUM_LENGTH as maxLength,
|
|
153
153
|
c.IS_NULLABLE as isNullable,
|
|
154
154
|
c.COLUMN_DEFAULT as defaultValue,
|
|
155
|
-
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey
|
|
155
|
+
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey,
|
|
156
|
+
fk.REFERENCED_TABLE_SCHEMA as referencedSchema,
|
|
157
|
+
fk.REFERENCED_TABLE_NAME as referencedTable,
|
|
158
|
+
fk.REFERENCED_COLUMN_NAME as referencedColumn
|
|
156
159
|
FROM INFORMATION_SCHEMA.COLUMNS c
|
|
157
160
|
LEFT JOIN (
|
|
158
161
|
SELECT ku.TABLE_SCHEMA, ku.TABLE_NAME, ku.COLUMN_NAME
|
|
@@ -163,6 +166,27 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
163
166
|
) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
|
|
164
167
|
AND c.TABLE_NAME = pk.TABLE_NAME
|
|
165
168
|
AND c.COLUMN_NAME = pk.COLUMN_NAME
|
|
169
|
+
LEFT JOIN (
|
|
170
|
+
SELECT
|
|
171
|
+
kcu1.TABLE_SCHEMA,
|
|
172
|
+
kcu1.TABLE_NAME,
|
|
173
|
+
kcu1.COLUMN_NAME,
|
|
174
|
+
kcu2.TABLE_SCHEMA as REFERENCED_TABLE_SCHEMA,
|
|
175
|
+
kcu2.TABLE_NAME as REFERENCED_TABLE_NAME,
|
|
176
|
+
kcu2.COLUMN_NAME as REFERENCED_COLUMN_NAME
|
|
177
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
|
178
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
|
|
179
|
+
ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
|
|
180
|
+
AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
|
|
181
|
+
AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
|
|
182
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
|
|
183
|
+
ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
|
|
184
|
+
AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
|
|
185
|
+
AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
|
|
186
|
+
AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
|
|
187
|
+
) fk ON c.TABLE_SCHEMA = fk.TABLE_SCHEMA
|
|
188
|
+
AND c.TABLE_NAME = fk.TABLE_NAME
|
|
189
|
+
AND c.COLUMN_NAME = fk.COLUMN_NAME
|
|
166
190
|
WHERE c.TABLE_SCHEMA = @schema
|
|
167
191
|
AND c.TABLE_NAME = @table
|
|
168
192
|
ORDER BY c.ORDINAL_POSITION
|
|
@@ -175,7 +199,7 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
|
|
|
175
199
|
} catch (error) {
|
|
176
200
|
const errorMessage = error.message || "";
|
|
177
201
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
178
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
202
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
179
203
|
await disconnect2();
|
|
180
204
|
}
|
|
181
205
|
res.status(500).json({ error: error.message || "Failed to fetch table structure" });
|
|
@@ -188,6 +212,7 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
|
|
|
188
212
|
const pageSize = Math.min(parseInt(req.query.pageSize) || 100, 1e3);
|
|
189
213
|
const sortColumn = req.query.sortColumn;
|
|
190
214
|
const sortDirection = req.query.sortDirection || "asc";
|
|
215
|
+
const fkDisplayMode = req.query.fkDisplayMode || "key-only";
|
|
191
216
|
const offset = (page - 1) * pageSize;
|
|
192
217
|
const filters = {};
|
|
193
218
|
Object.keys(req.query).forEach((key) => {
|
|
@@ -286,41 +311,179 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
|
|
|
286
311
|
} catch (e) {
|
|
287
312
|
}
|
|
288
313
|
}
|
|
314
|
+
const fkJoins = [];
|
|
315
|
+
const fkSelects = [];
|
|
316
|
+
const fkDisplayColumns = {};
|
|
317
|
+
if (fkDisplayMode === "key-display" || fkDisplayMode === "display-only") {
|
|
318
|
+
const fkQuery = `
|
|
319
|
+
SELECT
|
|
320
|
+
kcu1.COLUMN_NAME as fkColumnName,
|
|
321
|
+
kcu2.TABLE_SCHEMA as referencedSchema,
|
|
322
|
+
kcu2.TABLE_NAME as referencedTable,
|
|
323
|
+
kcu2.COLUMN_NAME as referencedColumn
|
|
324
|
+
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
|
325
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
|
|
326
|
+
ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
|
|
327
|
+
AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
|
|
328
|
+
AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
|
|
329
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
|
|
330
|
+
ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
|
|
331
|
+
AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
|
|
332
|
+
AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
|
|
333
|
+
AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
|
|
334
|
+
WHERE kcu1.TABLE_SCHEMA = @schema
|
|
335
|
+
AND kcu1.TABLE_NAME = @table
|
|
336
|
+
`;
|
|
337
|
+
console.log("Fetching foreign keys with query:", fkQuery);
|
|
338
|
+
const foreignKeys = await executeQuery(fkQuery, [
|
|
339
|
+
{ name: "schema", value: schema, type: sql.NVarChar },
|
|
340
|
+
{ name: "table", value: table, type: sql.NVarChar }
|
|
341
|
+
]);
|
|
342
|
+
console.log(`Found ${foreignKeys.length} foreign key(s)`);
|
|
343
|
+
if (foreignKeys.length > 0) {
|
|
344
|
+
const uniqueRefTables = Array.from(
|
|
345
|
+
new Set(foreignKeys.map((fk) => `${fk.referencedSchema}.${fk.referencedTable}`))
|
|
346
|
+
);
|
|
347
|
+
const tableConditions = uniqueRefTables.map((tableRef, idx) => {
|
|
348
|
+
const [refSchema, refTable] = tableRef.split(".");
|
|
349
|
+
return `(TABLE_SCHEMA = @refSchema${idx} AND TABLE_NAME = @refTable${idx})`;
|
|
350
|
+
}).join(" OR ");
|
|
351
|
+
const batchColumnsQuery = `
|
|
352
|
+
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION
|
|
353
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
354
|
+
WHERE ${tableConditions}
|
|
355
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
|
|
356
|
+
`;
|
|
357
|
+
const batchParams = uniqueRefTables.flatMap((tableRef, idx) => {
|
|
358
|
+
const [refSchema, refTable] = tableRef.split(".");
|
|
359
|
+
return [
|
|
360
|
+
{ name: `refSchema${idx}`, value: refSchema, type: sql.NVarChar },
|
|
361
|
+
{ name: `refTable${idx}`, value: refTable, type: sql.NVarChar }
|
|
362
|
+
];
|
|
363
|
+
});
|
|
364
|
+
console.log("Fetching referenced table columns with query:", batchColumnsQuery);
|
|
365
|
+
console.log("Batch parameters:", batchParams.map((p) => ({ name: p.name, value: p.value })));
|
|
366
|
+
const allRefColumns = await executeQuery(batchColumnsQuery, batchParams);
|
|
367
|
+
console.log(`Found columns for ${uniqueRefTables.length} referenced table(s)`);
|
|
368
|
+
const columnsByTable = {};
|
|
369
|
+
allRefColumns.forEach((col) => {
|
|
370
|
+
const key = `${col.TABLE_SCHEMA}.${col.TABLE_NAME}`;
|
|
371
|
+
if (!columnsByTable[key]) {
|
|
372
|
+
columnsByTable[key] = [];
|
|
373
|
+
}
|
|
374
|
+
columnsByTable[key].push(col);
|
|
375
|
+
});
|
|
376
|
+
for (const fk of foreignKeys) {
|
|
377
|
+
const fkColumn = fk.fkColumnName;
|
|
378
|
+
const refSchema = fk.referencedSchema;
|
|
379
|
+
const refTable = fk.referencedTable;
|
|
380
|
+
const refColumn = fk.referencedColumn;
|
|
381
|
+
const tableKey = `${refSchema}.${refTable}`;
|
|
382
|
+
const refColumns = columnsByTable[tableKey] || [];
|
|
383
|
+
const preferredNames = ["name", "title", "description", "code"];
|
|
384
|
+
let displayColumn = null;
|
|
385
|
+
for (const preferredName of preferredNames) {
|
|
386
|
+
const found = refColumns.find(
|
|
387
|
+
(col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
|
|
388
|
+
);
|
|
389
|
+
if (found) {
|
|
390
|
+
displayColumn = found.COLUMN_NAME;
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (!displayColumn) {
|
|
395
|
+
const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
|
|
396
|
+
const found = refColumns.find(
|
|
397
|
+
(col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
|
|
398
|
+
);
|
|
399
|
+
if (found) {
|
|
400
|
+
displayColumn = found.COLUMN_NAME;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (displayColumn) {
|
|
404
|
+
const alias = `fk_${fkColumn}`;
|
|
405
|
+
fkJoins.push({
|
|
406
|
+
alias,
|
|
407
|
+
refSchema,
|
|
408
|
+
refTable,
|
|
409
|
+
fkColumn,
|
|
410
|
+
refColumn,
|
|
411
|
+
displayColumn
|
|
412
|
+
});
|
|
413
|
+
fkSelects.push(`${alias}.[${displayColumn}] as [${fkColumn}_display]`);
|
|
414
|
+
fkDisplayColumns[fkColumn] = displayColumn;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const baseTableAlias = "t";
|
|
420
|
+
let baseSelects = `[${baseTableAlias}].*`;
|
|
421
|
+
if (fkDisplayMode === "display-only") {
|
|
422
|
+
const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
|
|
423
|
+
if (fkColumnNames.length > 0) {
|
|
424
|
+
baseSelects = `[${baseTableAlias}].*`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const allSelects = `${baseSelects}${fkSelects.length > 0 ? ", " + fkSelects.join(", ") : ""}`;
|
|
428
|
+
const buildJoinClauses = (tableAlias) => {
|
|
429
|
+
return fkJoins.map(
|
|
430
|
+
(fk) => `LEFT JOIN [${fk.refSchema}].[${fk.refTable}] ${fk.alias} ON [${tableAlias}].[${fk.fkColumn}] = ${fk.alias}.[${fk.refColumn}]`
|
|
431
|
+
).join("\n ");
|
|
432
|
+
};
|
|
289
433
|
let data;
|
|
290
434
|
let generatedQuery = "";
|
|
291
435
|
if (orderByColumn) {
|
|
292
|
-
generatedQuery = `SELECT * FROM [${schema}].[${table}]${whereClause ? "\n" + whereClause : ""}
|
|
293
|
-
ORDER BY [${orderByColumn}] ${orderByDirection}
|
|
294
|
-
OFFSET ${offset} ROWS
|
|
295
|
-
FETCH NEXT ${pageSize} ROWS ONLY`;
|
|
296
436
|
const dataQuery = `
|
|
297
|
-
SELECT
|
|
437
|
+
SELECT ${allSelects}
|
|
438
|
+
FROM [${schema}].[${table}] ${baseTableAlias}
|
|
439
|
+
${buildJoinClauses(baseTableAlias)}
|
|
298
440
|
${whereClause}
|
|
299
|
-
ORDER BY [${orderByColumn}] ${orderByDirection}
|
|
441
|
+
ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
|
|
300
442
|
OFFSET @offset ROWS
|
|
301
443
|
FETCH NEXT @pageSize ROWS ONLY
|
|
302
444
|
`;
|
|
445
|
+
generatedQuery = `SELECT ${allSelects}
|
|
446
|
+
FROM [${schema}].[${table}] ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${whereClause ? "\n" + whereClause : ""}
|
|
447
|
+
ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
|
|
448
|
+
OFFSET ${offset} ROWS
|
|
449
|
+
FETCH NEXT ${pageSize} ROWS ONLY`;
|
|
450
|
+
console.log("Executing SQL query:", dataQuery);
|
|
451
|
+
console.log("Query parameters:", {
|
|
452
|
+
offset,
|
|
453
|
+
pageSize,
|
|
454
|
+
filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
|
|
455
|
+
});
|
|
303
456
|
data = await executeQuery(dataQuery, [
|
|
304
457
|
{ name: "offset", value: offset, type: sql.Int },
|
|
305
458
|
{ name: "pageSize", value: pageSize, type: sql.Int },
|
|
306
459
|
...filterParams
|
|
307
460
|
]);
|
|
308
461
|
} else {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
ORDER BY rn`;
|
|
462
|
+
const innerQuery = `
|
|
463
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
|
|
464
|
+
FROM [${schema}].[${table}]
|
|
465
|
+
${whereClause}
|
|
466
|
+
`;
|
|
315
467
|
const dataQuery = `
|
|
316
|
-
SELECT
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
WHERE rn > @offset AND rn <= @offset + @pageSize
|
|
322
|
-
ORDER BY rn
|
|
468
|
+
SELECT ${allSelects}
|
|
469
|
+
FROM (${innerQuery}) ${baseTableAlias}
|
|
470
|
+
${buildJoinClauses(baseTableAlias)}
|
|
471
|
+
WHERE ${baseTableAlias}.rn > @offset AND ${baseTableAlias}.rn <= @offset + @pageSize
|
|
472
|
+
ORDER BY ${baseTableAlias}.rn
|
|
323
473
|
`;
|
|
474
|
+
generatedQuery = `SELECT ${allSelects}
|
|
475
|
+
FROM (
|
|
476
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
|
|
477
|
+
FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
|
|
478
|
+
) ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}
|
|
479
|
+
WHERE ${baseTableAlias}.rn > ${offset} AND ${baseTableAlias}.rn <= ${offset + pageSize}
|
|
480
|
+
ORDER BY ${baseTableAlias}.rn`;
|
|
481
|
+
console.log("Executing SQL query:", dataQuery);
|
|
482
|
+
console.log("Query parameters:", {
|
|
483
|
+
offset,
|
|
484
|
+
pageSize,
|
|
485
|
+
filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
|
|
486
|
+
});
|
|
324
487
|
data = await executeQuery(dataQuery, [
|
|
325
488
|
{ name: "offset", value: offset, type: sql.Int },
|
|
326
489
|
{ name: "pageSize", value: pageSize, type: sql.Int },
|
|
@@ -331,6 +494,21 @@ ORDER BY rn`;
|
|
|
331
494
|
return rest;
|
|
332
495
|
});
|
|
333
496
|
}
|
|
497
|
+
if (fkDisplayMode === "display-only") {
|
|
498
|
+
const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
|
|
499
|
+
data = data.map((row) => {
|
|
500
|
+
const filteredRow = { ...row };
|
|
501
|
+
fkColumnNames.forEach((fkCol) => {
|
|
502
|
+
delete filteredRow[fkCol];
|
|
503
|
+
const displayColName = `${fkCol}_display`;
|
|
504
|
+
if (displayColName in filteredRow) {
|
|
505
|
+
filteredRow[fkCol] = filteredRow[displayColName];
|
|
506
|
+
delete filteredRow[displayColName];
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
return filteredRow;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
334
512
|
res.json({
|
|
335
513
|
data,
|
|
336
514
|
query: generatedQuery,
|
|
@@ -339,20 +517,124 @@ ORDER BY rn`;
|
|
|
339
517
|
pageSize,
|
|
340
518
|
total,
|
|
341
519
|
totalPages: Math.ceil(total / pageSize)
|
|
342
|
-
}
|
|
520
|
+
},
|
|
521
|
+
foreignKeyDisplays: fkDisplayColumns,
|
|
522
|
+
fkDisplayMode
|
|
343
523
|
});
|
|
344
524
|
} catch (error) {
|
|
345
525
|
console.error("Error fetching table data:", error);
|
|
346
526
|
const errorMessage = error.message || "";
|
|
347
527
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
348
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
528
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
349
529
|
await disconnect2();
|
|
350
530
|
}
|
|
531
|
+
const isTimeout = error.code === "ETIMEOUT" || error.code === "ESOCKET" || error.message?.includes("timeout") || error.message?.includes("ETIMEDOUT") || error.originalError?.code === "ETIMEOUT" || error.originalError?.code === "ESOCKET";
|
|
351
532
|
const errorDetails = error.originalError?.message || error.originalError?.info?.message || "";
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
533
|
+
if (isTimeout) {
|
|
534
|
+
res.status(408).json({
|
|
535
|
+
error: errorMessage || "Query execution timeout",
|
|
536
|
+
details: errorDetails || "The query took too long to execute. Try disabling foreign key displays or reducing the page size.",
|
|
537
|
+
timeout: true
|
|
538
|
+
});
|
|
539
|
+
} else {
|
|
540
|
+
res.status(500).json({
|
|
541
|
+
error: errorMessage,
|
|
542
|
+
details: errorDetails
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
|
|
548
|
+
try {
|
|
549
|
+
const { schema, table } = req.params;
|
|
550
|
+
const { foreignKeyColumn, referencedSchema, referencedTable, referencedColumn, ids } = req.body;
|
|
551
|
+
if (!foreignKeyColumn || !referencedSchema || !referencedTable || !referencedColumn || !ids || !Array.isArray(ids) || ids.length === 0) {
|
|
552
|
+
return res.status(400).json({ error: "Missing required parameters" });
|
|
553
|
+
}
|
|
554
|
+
const pool = getConnection();
|
|
555
|
+
if (!pool || !pool.connected) {
|
|
556
|
+
return res.status(400).json({ error: "Not connected to database" });
|
|
557
|
+
}
|
|
558
|
+
const columnsQuery = `
|
|
559
|
+
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
|
|
560
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
561
|
+
WHERE TABLE_SCHEMA = @refSchema
|
|
562
|
+
AND TABLE_NAME = @refTable
|
|
563
|
+
ORDER BY ORDINAL_POSITION
|
|
564
|
+
`;
|
|
565
|
+
const columns = await executeQuery(columnsQuery, [
|
|
566
|
+
{ name: "refSchema", value: referencedSchema, type: sql.NVarChar },
|
|
567
|
+
{ name: "refTable", value: referencedTable, type: sql.NVarChar }
|
|
568
|
+
]);
|
|
569
|
+
const referencedColInfo = columns.find(
|
|
570
|
+
(col) => col.COLUMN_NAME === referencedColumn
|
|
571
|
+
);
|
|
572
|
+
if (!referencedColInfo) {
|
|
573
|
+
return res.status(400).json({ error: `Referenced column '${referencedColumn}' not found` });
|
|
574
|
+
}
|
|
575
|
+
const getSqlType = (dataType) => {
|
|
576
|
+
const dt = dataType.toLowerCase();
|
|
577
|
+
if (dt === "int" || dt === "integer") return sql.Int;
|
|
578
|
+
if (dt === "bigint") return sql.BigInt;
|
|
579
|
+
if (dt === "smallint") return sql.SmallInt;
|
|
580
|
+
if (dt === "tinyint") return sql.TinyInt;
|
|
581
|
+
if (dt === "bit") return sql.Bit;
|
|
582
|
+
if (dt === "float" || dt === "real" || dt === "double precision") return sql.Float;
|
|
583
|
+
if (dt === "decimal" || dt === "numeric" || dt === "money" || dt === "smallmoney") return sql.Decimal(18, 0);
|
|
584
|
+
if (dt === "datetime" || dt === "datetime2" || dt === "smalldatetime") return sql.DateTime;
|
|
585
|
+
if (dt === "date") return sql.Date;
|
|
586
|
+
if (dt === "time") return sql.Time;
|
|
587
|
+
if (dt === "uniqueidentifier") return sql.UniqueIdentifier;
|
|
588
|
+
return sql.NVarChar;
|
|
589
|
+
};
|
|
590
|
+
const referencedColumnType = getSqlType(referencedColInfo.DATA_TYPE);
|
|
591
|
+
const preferredNames = ["name", "title", "description", "code"];
|
|
592
|
+
let displayColumn = null;
|
|
593
|
+
for (const preferredName of preferredNames) {
|
|
594
|
+
const found = columns.find(
|
|
595
|
+
(col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
|
|
596
|
+
);
|
|
597
|
+
if (found) {
|
|
598
|
+
displayColumn = found.COLUMN_NAME;
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (!displayColumn) {
|
|
603
|
+
const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
|
|
604
|
+
const found = columns.find(
|
|
605
|
+
(col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
|
|
606
|
+
);
|
|
607
|
+
if (found) {
|
|
608
|
+
displayColumn = found.COLUMN_NAME;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const placeholders = ids.map((_, i) => `@id${i}`).join(", ");
|
|
612
|
+
const selectColumns = displayColumn ? `[${referencedColumn}], [${displayColumn}]` : `[${referencedColumn}]`;
|
|
613
|
+
const dataQuery = `
|
|
614
|
+
SELECT ${selectColumns}
|
|
615
|
+
FROM [${referencedSchema}].[${referencedTable}]
|
|
616
|
+
WHERE [${referencedColumn}] IN (${placeholders})
|
|
617
|
+
`;
|
|
618
|
+
const params = ids.map((id, i) => ({
|
|
619
|
+
name: `id${i}`,
|
|
620
|
+
value: id,
|
|
621
|
+
type: referencedColumnType
|
|
622
|
+
}));
|
|
623
|
+
const result = await executeQuery(dataQuery, params);
|
|
624
|
+
const dataMap = {};
|
|
625
|
+
result.forEach((row) => {
|
|
626
|
+
const key = String(row[referencedColumn]);
|
|
627
|
+
dataMap[key] = displayColumn ? row[displayColumn] : null;
|
|
355
628
|
});
|
|
629
|
+
res.json({ dataMap, displayColumn });
|
|
630
|
+
} catch (error) {
|
|
631
|
+
console.error("Error fetching related data:", error);
|
|
632
|
+
const errorMessage = error.message || "";
|
|
633
|
+
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
634
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
635
|
+
await disconnect2();
|
|
636
|
+
}
|
|
637
|
+
res.status(500).json({ error: error.message || "Failed to fetch related data" });
|
|
356
638
|
}
|
|
357
639
|
});
|
|
358
640
|
|
|
@@ -365,34 +647,20 @@ queryRoutes.post("/", async (req, res) => {
|
|
|
365
647
|
if (!sqlQuery || typeof sqlQuery !== "string") {
|
|
366
648
|
return res.status(400).json({ error: "Query is required" });
|
|
367
649
|
}
|
|
368
|
-
const trimmedQuery = sqlQuery.trim().toUpperCase();
|
|
369
|
-
if (!trimmedQuery.startsWith("SELECT")) {
|
|
370
|
-
return res.status(400).json({
|
|
371
|
-
error: "Only SELECT queries are allowed (read-only mode)"
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
const dangerousKeywords = ["DROP", "DELETE", "INSERT", "UPDATE", "ALTER", "CREATE", "TRUNCATE", "EXEC", "EXECUTE"];
|
|
375
|
-
const hasDangerousKeyword = dangerousKeywords.some((keyword) => {
|
|
376
|
-
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
377
|
-
return regex.test(sqlQuery);
|
|
378
|
-
});
|
|
379
|
-
if (hasDangerousKeyword) {
|
|
380
|
-
return res.status(400).json({
|
|
381
|
-
error: "Query contains prohibited keywords. Only SELECT queries are allowed."
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
650
|
const startTime = Date.now();
|
|
385
|
-
const resultSets = await executeQueryMultiple(sqlQuery);
|
|
651
|
+
const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery);
|
|
386
652
|
const executionTime = Date.now() - startTime;
|
|
387
653
|
res.json({
|
|
388
654
|
data: resultSets[0] || [],
|
|
389
655
|
resultSets: resultSets.length > 0 ? resultSets : [],
|
|
390
|
-
executionTime
|
|
656
|
+
executionTime,
|
|
657
|
+
columnMetadata
|
|
658
|
+
// Include column metadata for empty result sets
|
|
391
659
|
});
|
|
392
660
|
} catch (error) {
|
|
393
661
|
const errorMessage = error.message || "";
|
|
394
662
|
if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
|
|
395
|
-
const { disconnect: disconnect2 } = await import("./mssql-
|
|
663
|
+
const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
|
|
396
664
|
await disconnect2();
|
|
397
665
|
}
|
|
398
666
|
res.status(500).json({
|