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.
Files changed (32) hide show
  1. package/dist/cli/chunk-3XB4P3G3.js +228 -0
  2. package/dist/cli/{chunk-5X2YZYYM.js → chunk-G7KSYREO.js} +43 -5
  3. package/dist/cli/chunk-Y3EHCZQX.js +220 -0
  4. package/dist/cli/{chunk-PTI2CUG6.js → chunk-Z2LV7EWL.js} +75 -4
  5. package/dist/cli/dev.js +317 -49
  6. package/dist/cli/index.js +317 -49
  7. package/dist/cli/{mssql-D7NDIUG7.js → mssql-AYS72PRQ.js} +1 -1
  8. package/dist/cli/{mssql-5VFXLOGV.js → mssql-HI3S4B7E.js} +3 -1
  9. package/dist/cli/{mssql-4KECNFPA.js → mssql-JB63TSGG.js} +4 -1
  10. package/dist/cli/mssql-Y5A62OYG.js +20 -0
  11. package/dist/client/assets/index-BhZ7NrhL.css +1 -0
  12. package/dist/client/assets/index-D-mash8_.js +370 -0
  13. package/dist/client/index.html +2 -2
  14. package/dist/server/{chunk-XFS5KTMI.js → chunk-JYZHE6GB.js} +43 -5
  15. package/dist/server/{chunk-7G2KHS5I.js → chunk-MJJGJZFC.js} +75 -4
  16. package/dist/server/chunk-VEX42Z2L.js +225 -0
  17. package/dist/server/chunk-VQPGGH2I.js +217 -0
  18. package/dist/server/dev.js +317 -49
  19. package/dist/server/index.js +317 -49
  20. package/dist/server/{mssql-UUT2IKOP.js → mssql-H6LKT5KN.js} +1 -1
  21. package/dist/server/mssql-JDRJT5H4.js +18 -0
  22. package/dist/server/mssql-LLTFKNX2.js +18 -0
  23. package/dist/server/mssql-UZUN227W.js +18 -0
  24. package/package.json +4 -3
  25. package/dist/cli/chunk-4IGBRCNN.js +0 -118
  26. package/dist/cli/chunk-IPR5JXB5.js +0 -108
  27. package/dist/cli/mssql-2REU4IX7.js +0 -17
  28. package/dist/client/assets/index-C32MC3if.js +0 -239
  29. package/dist/client/assets/index-piPKidzx.css +0 -1
  30. package/dist/server/chunk-XMPF5YCJ.js +0 -106
  31. package/dist/server/mssql-7KV6MCZK.js +0 -16
  32. package/dist/server/mssql-XRYTOVSF.js +0 -16
@@ -5,7 +5,7 @@ import {
5
5
  executeQueryMultiple,
6
6
  getConnection,
7
7
  testConnection
8
- } from "./chunk-XFS5KTMI.js";
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-UUT2IKOP.js");
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-UUT2IKOP.js");
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-UUT2IKOP.js");
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-UUT2IKOP.js");
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 * FROM [${schema}].[${table}]
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
- generatedQuery = `SELECT * FROM (
310
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
311
- FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
312
- ) t
313
- WHERE rn > ${offset} AND rn <= ${offset + pageSize}
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 * FROM (
317
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
318
- FROM [${schema}].[${table}]
319
- ${whereClause}
320
- ) t
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-UUT2IKOP.js");
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
- res.status(500).json({
353
- error: errorMessage,
354
- details: errorDetails
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-UUT2IKOP.js");
663
+ const { disconnect: disconnect2 } = await import("./mssql-UZUN227W.js");
396
664
  await disconnect2();
397
665
  }
398
666
  res.status(500).json({