datapeek 0.1.8 → 0.1.9

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.
@@ -71,7 +71,23 @@ async function testConnection(config) {
71
71
  async function connect(config) {
72
72
  await disconnect();
73
73
  const connectionConfig = typeof config === "string" ? parseConnectionString(config) : config;
74
- pool = new sql.ConnectionPool(connectionConfig);
74
+ const poolConfig = {
75
+ ...connectionConfig,
76
+ connectionTimeout: 3e4,
77
+ // 30 seconds for connection
78
+ pool: {
79
+ max: 10,
80
+ min: 0,
81
+ idleTimeoutMillis: 3e4,
82
+ acquireTimeoutMillis: 3e4
83
+ },
84
+ options: {
85
+ ...connectionConfig.options,
86
+ requestTimeout: 12e4
87
+ // 2 minutes for queries (increased for complex queries with JOINs)
88
+ }
89
+ };
90
+ pool = new sql.ConnectionPool(poolConfig);
75
91
  try {
76
92
  await pool.connect();
77
93
  } catch (error) {
@@ -96,6 +112,7 @@ async function executeQuery(query, parameters) {
96
112
  throw new Error("Not connected to database");
97
113
  }
98
114
  const request = pool.request();
115
+ request.timeout = 12e4;
99
116
  if (parameters) {
100
117
  for (const param of parameters) {
101
118
  if (param.type) {
@@ -105,14 +122,25 @@ async function executeQuery(query, parameters) {
105
122
  }
106
123
  }
107
124
  }
108
- const result = await request.query(query);
109
- return result.recordset || [];
125
+ try {
126
+ const result = await request.query(query);
127
+ return result.recordset || [];
128
+ } catch (error) {
129
+ if (error.code === "ETIMEOUT" || error.code === "ESOCKET" || error.message?.includes("timeout") || error.message?.includes("ETIMEDOUT")) {
130
+ const timeoutError = new Error("Query execution timeout. The query took too long to execute. Try disabling foreign key displays or reducing the page size.");
131
+ timeoutError.code = "ETIMEOUT";
132
+ timeoutError.originalError = error;
133
+ throw timeoutError;
134
+ }
135
+ throw error;
136
+ }
110
137
  }
111
138
  async function executeQueryMultiple(query, parameters) {
112
139
  if (!pool || !pool.connected) {
113
140
  throw new Error("Not connected to database");
114
141
  }
115
142
  const request = pool.request();
143
+ request.timeout = 12e4;
116
144
  if (parameters) {
117
145
  for (const param of parameters) {
118
146
  if (param.type) {
@@ -122,8 +150,18 @@ async function executeQueryMultiple(query, parameters) {
122
150
  }
123
151
  }
124
152
  }
125
- const result = await request.query(query);
126
- return result.recordsets || [];
153
+ try {
154
+ const result = await request.query(query);
155
+ return result.recordsets || [];
156
+ } catch (error) {
157
+ if (error.code === "ETIMEOUT" || error.code === "ESOCKET" || error.message?.includes("timeout") || error.message?.includes("ETIMEDOUT")) {
158
+ const timeoutError = new Error("Query execution timeout. The query took too long to execute. Try simplifying your query or reducing the result set size.");
159
+ timeoutError.code = "ETIMEOUT";
160
+ timeoutError.originalError = error;
161
+ throw timeoutError;
162
+ }
163
+ throw error;
164
+ }
127
165
  }
128
166
 
129
167
  export {
package/dist/cli/dev.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  executeQueryMultiple,
8
8
  getConnection,
9
9
  testConnection
10
- } from "./chunk-5X2YZYYM.js";
10
+ } from "./chunk-G7KSYREO.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-D7NDIUG7.js");
91
+ const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-HI3S4B7E.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-D7NDIUG7.js");
101
+ const { disconnect: disconnect2 } = await import("./mssql-HI3S4B7E.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-D7NDIUG7.js");
137
+ const { disconnect: disconnect2 } = await import("./mssql-HI3S4B7E.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-D7NDIUG7.js");
204
+ const { disconnect: disconnect2 } = await import("./mssql-HI3S4B7E.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 * FROM [${schema}].[${table}]
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
- generatedQuery = `SELECT * FROM (
312
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
313
- FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
314
- ) t
315
- WHERE rn > ${offset} AND rn <= ${offset + pageSize}
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 * FROM (
319
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
320
- FROM [${schema}].[${table}]
321
- ${whereClause}
322
- ) t
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-D7NDIUG7.js");
530
+ const { disconnect: disconnect2 } = await import("./mssql-HI3S4B7E.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
- res.status(500).json({
355
- error: errorMessage,
356
- details: errorDetails
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-HI3S4B7E.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,22 +649,6 @@ 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
653
  const resultSets = await executeQueryMultiple(sqlQuery);
388
654
  const executionTime = Date.now() - startTime;
@@ -394,7 +660,7 @@ queryRoutes.post("/", async (req, res) => {
394
660
  } catch (error) {
395
661
  const errorMessage = error.message || "";
396
662
  if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
397
- const { disconnect: disconnect2 } = await import("./mssql-D7NDIUG7.js");
663
+ const { disconnect: disconnect2 } = await import("./mssql-HI3S4B7E.js");
398
664
  await disconnect2();
399
665
  }
400
666
  res.status(500).json({