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