datapeek 0.1.10 → 0.1.12

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 (92) hide show
  1. package/dist/cli/chunk-2I4MNNVK.js +628 -0
  2. package/dist/cli/chunk-4GDQ7VJZ.js +662 -0
  3. package/dist/cli/chunk-4MIQHH6K.js +47 -0
  4. package/dist/cli/chunk-6K3WSEIM.js +375 -0
  5. package/dist/cli/chunk-6PMCDR7J.js +628 -0
  6. package/dist/cli/chunk-B7K46745.js +605 -0
  7. package/dist/cli/chunk-CK2O76SG.js +713 -0
  8. package/dist/cli/chunk-FXOMCTFV.js +696 -0
  9. package/dist/cli/chunk-G4ID5SFT.js +628 -0
  10. package/dist/cli/chunk-GCVFEHSZ.js +708 -0
  11. package/dist/cli/chunk-LGE2JQ3S.js +628 -0
  12. package/dist/cli/chunk-NDLKLB5A.js +710 -0
  13. package/dist/cli/chunk-PG6I26XT.js +614 -0
  14. package/dist/cli/chunk-RK2Q2AX5.js +582 -0
  15. package/dist/cli/chunk-S5LRCCK6.js +582 -0
  16. package/dist/cli/chunk-UBFQXPPF.js +701 -0
  17. package/dist/cli/chunk-X2DQBOP6.js +712 -0
  18. package/dist/cli/chunk-XRMPVZIS.js +709 -0
  19. package/dist/cli/chunk-Z5G3UEXN.js +711 -0
  20. package/dist/cli/chunk-ZMP7S3G6.js +696 -0
  21. package/dist/cli/db-32TDDTFP.js +28 -0
  22. package/dist/cli/db-47O74DN5.js +34 -0
  23. package/dist/cli/db-4U22OWGB.js +29 -0
  24. package/dist/cli/db-CL72XDNO.js +34 -0
  25. package/dist/cli/db-CZCZOEID.js +28 -0
  26. package/dist/cli/db-IX3DXZ2C.js +34 -0
  27. package/dist/cli/db-LJCRTZTY.js +34 -0
  28. package/dist/cli/db-LNX4UMRF.js +28 -0
  29. package/dist/cli/db-NESFXDWL.js +28 -0
  30. package/dist/cli/db-OIJ2PJK6.js +34 -0
  31. package/dist/cli/db-OPAF6ZO3.js +28 -0
  32. package/dist/cli/db-PQURQHSK.js +28 -0
  33. package/dist/cli/db-QN5MTDYB.js +28 -0
  34. package/dist/cli/db-RBMUR3OK.js +34 -0
  35. package/dist/cli/db-REMUYLPP.js +34 -0
  36. package/dist/cli/db-RTHQ4ZAU.js +28 -0
  37. package/dist/cli/db-SHFLGGWD.js +28 -0
  38. package/dist/cli/db-ULMGZXQ5.js +34 -0
  39. package/dist/cli/db-YOGJ4LXE.js +28 -0
  40. package/dist/cli/dev.js +717 -291
  41. package/dist/cli/index.js +720 -293
  42. package/dist/cli/queryCancellation-4CMKYTFU.js +46 -0
  43. package/dist/cli/queryCancellation-E5O4FBUY.js +14 -0
  44. package/dist/client/assets/index-BXssAVuE.css +1 -0
  45. package/dist/client/assets/index-DgwSpfvt.js +378 -0
  46. package/dist/client/index.html +2 -2
  47. package/dist/server/chunk-26GSUAI4.js +710 -0
  48. package/dist/server/chunk-36OOXHKI.js +659 -0
  49. package/dist/server/chunk-3DAIYH42.js +44 -0
  50. package/dist/server/chunk-4ELMZYWA.js +611 -0
  51. package/dist/server/chunk-5ESS5YBD.js +707 -0
  52. package/dist/server/chunk-7HIBWXBL.js +625 -0
  53. package/dist/server/chunk-BH22Q2FV.js +705 -0
  54. package/dist/server/chunk-CFU2CLKS.js +693 -0
  55. package/dist/server/chunk-CXA3HXTK.js +698 -0
  56. package/dist/server/chunk-DAUUXYQF.js +706 -0
  57. package/dist/server/chunk-FMSIEQ2T.js +579 -0
  58. package/dist/server/chunk-G6A463RM.js +625 -0
  59. package/dist/server/chunk-H6DUOXOK.js +625 -0
  60. package/dist/server/chunk-JCRZ6H7P.js +579 -0
  61. package/dist/server/chunk-L6LL3NE7.js +602 -0
  62. package/dist/server/chunk-TH6TUILG.js +709 -0
  63. package/dist/server/chunk-UJ2LRK25.js +373 -0
  64. package/dist/server/chunk-YI67PW64.js +625 -0
  65. package/dist/server/chunk-ZDZ2C5ZQ.js +693 -0
  66. package/dist/server/chunk-ZJOVVCPT.js +708 -0
  67. package/dist/server/db-22DQ6YG7.js +32 -0
  68. package/dist/server/db-3CBO6RHU.js +26 -0
  69. package/dist/server/db-3XN2KWYT.js +32 -0
  70. package/dist/server/db-3Y6K4EDP.js +26 -0
  71. package/dist/server/db-57HXNWPD.js +26 -0
  72. package/dist/server/db-5GCTDKYH.js +27 -0
  73. package/dist/server/db-5VQOTJ52.js +26 -0
  74. package/dist/server/db-B5L7VQ7E.js +32 -0
  75. package/dist/server/db-CJPCGHL3.js +32 -0
  76. package/dist/server/db-DAQ4BVCU.js +32 -0
  77. package/dist/server/db-EEA6EWHL.js +26 -0
  78. package/dist/server/db-KR7Z5M5V.js +32 -0
  79. package/dist/server/db-LMSBISDM.js +26 -0
  80. package/dist/server/db-LSOQE7N7.js +32 -0
  81. package/dist/server/db-NHBP6YLH.js +26 -0
  82. package/dist/server/db-OWVJVBBD.js +26 -0
  83. package/dist/server/db-TTVXKX7P.js +26 -0
  84. package/dist/server/db-YJ753EQV.js +26 -0
  85. package/dist/server/db-ZV6GZHFU.js +32 -0
  86. package/dist/server/dev.js +717 -291
  87. package/dist/server/index.js +717 -291
  88. package/dist/server/queryCancellation-M56DISJX.js +43 -0
  89. package/dist/server/queryCancellation-T2HSNMIV.js +12 -0
  90. package/package.json +3 -1
  91. package/dist/client/assets/index-BhZ7NrhL.css +0 -1
  92. package/dist/client/assets/index-D-mash8_.js +0 -370
package/dist/cli/index.js CHANGED
@@ -6,8 +6,15 @@ import {
6
6
  executeQuery,
7
7
  executeQueryMultiple,
8
8
  getConnection,
9
+ getDbType,
10
+ getDialect,
11
+ setDbType,
9
12
  testConnection
10
- } from "./chunk-3XB4P3G3.js";
13
+ } from "./chunk-CK2O76SG.js";
14
+ import {
15
+ cancelQuery,
16
+ generateQueryId
17
+ } from "./chunk-4MIQHH6K.js";
11
18
 
12
19
  // src/cli/index.ts
13
20
  import { Command } from "commander";
@@ -30,9 +37,12 @@ connectionRoutes.post("/test", async (req, res) => {
30
37
  message: "Connection test timeout"
31
38
  });
32
39
  }
33
- }, 1e4);
40
+ }, 18e5);
34
41
  try {
35
42
  const config = req.body;
43
+ if (config.dbType) {
44
+ setDbType(config.dbType);
45
+ }
36
46
  await testConnection(config);
37
47
  clearTimeout(timeout);
38
48
  if (!res.headersSent) {
@@ -60,9 +70,12 @@ connectionRoutes.post("/", async (req, res) => {
60
70
  message: "Connection timeout"
61
71
  });
62
72
  }
63
- }, 15e3);
73
+ }, 18e5);
64
74
  try {
65
75
  const config = req.body;
76
+ if (config.dbType) {
77
+ setDbType(config.dbType);
78
+ }
66
79
  await connect(config);
67
80
  clearTimeout(timeout);
68
81
  if (!res.headersSent) {
@@ -91,19 +104,28 @@ connectionRoutes.delete("/", async (req, res) => {
91
104
  });
92
105
  connectionRoutes.get("/status", async (req, res) => {
93
106
  try {
94
- const { getConnection: getConnection2, executeQuery: executeQuery3 } = await import("./mssql-AYS72PRQ.js");
95
- const pool = getConnection2();
96
- if (pool && pool.connected) {
97
- try {
98
- const result = await executeQuery3("SELECT DB_NAME() as databaseName");
99
- const databaseName = result[0]?.databaseName || null;
100
- res.json({ connected: true, databaseName });
101
- } catch (error) {
102
- const errorMessage = error.message || "";
103
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
104
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
105
- await disconnect2();
107
+ const pool = getConnection();
108
+ if (pool) {
109
+ let isConnected = false;
110
+ if ("connected" in pool) {
111
+ isConnected = pool.connected === true;
112
+ } else {
113
+ isConnected = !pool.ended;
114
+ }
115
+ if (isConnected) {
116
+ try {
117
+ const dialect = getDialect();
118
+ const result = await executeQuery(dialect.currentDbQuery());
119
+ const databaseName = result[0]?.databaseName || null;
120
+ res.json({ connected: true, databaseName });
121
+ } catch (error) {
122
+ const errorMessage = error.message || "";
123
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
124
+ await disconnect();
125
+ }
126
+ res.json({ connected: false });
106
127
  }
128
+ } else {
107
129
  res.json({ connected: false });
108
130
  }
109
131
  } else {
@@ -116,28 +138,34 @@ connectionRoutes.get("/status", async (req, res) => {
116
138
 
117
139
  // src/server/routes/tables.ts
118
140
  import { Router as Router2 } from "express";
119
- import sql from "mssql";
120
141
  var tableRoutes = Router2();
121
142
  tableRoutes.get("/", async (req, res) => {
122
143
  try {
123
144
  const pool = getConnection();
124
- if (!pool || !pool.connected) {
145
+ if (!pool) {
146
+ return res.status(400).json({ error: "Not connected to database" });
147
+ }
148
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
149
+ if (!isConnected) {
125
150
  return res.status(400).json({ error: "Not connected to database" });
126
151
  }
152
+ const dialect = getDialect();
153
+ const dbType = getDbType();
154
+ const schemaFilter = dbType === "postgres" ? `AND table_schema NOT IN ('pg_catalog', 'information_schema')` : "";
127
155
  const query = `
128
156
  SELECT
129
- TABLE_SCHEMA as schemaName,
130
- TABLE_NAME as tableName
131
- FROM INFORMATION_SCHEMA.TABLES
132
- WHERE TABLE_TYPE = 'BASE TABLE'
133
- ORDER BY TABLE_SCHEMA, TABLE_NAME
157
+ table_schema as "schemaName",
158
+ table_name as "tableName"
159
+ FROM information_schema.tables
160
+ WHERE table_type = 'BASE TABLE'${schemaFilter}
161
+ ORDER BY table_schema, table_name
134
162
  `;
135
163
  const result = await executeQuery(query);
136
164
  res.json(result);
137
165
  } catch (error) {
138
166
  const errorMessage = error.message || "";
139
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
140
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
167
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
168
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
141
169
  await disconnect2();
142
170
  }
143
171
  res.status(500).json({ error: error.message || "Failed to fetch tables" });
@@ -147,64 +175,69 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
147
175
  try {
148
176
  const { schema, table } = req.params;
149
177
  const pool = getConnection();
150
- if (!pool || !pool.connected) {
178
+ if (!pool) {
179
+ return res.status(400).json({ error: "Not connected to database" });
180
+ }
181
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
182
+ if (!isConnected) {
151
183
  return res.status(400).json({ error: "Not connected to database" });
152
184
  }
185
+ const dialect = getDialect();
153
186
  const query = `
154
187
  SELECT
155
- c.COLUMN_NAME as columnName,
156
- c.DATA_TYPE as dataType,
157
- c.CHARACTER_MAXIMUM_LENGTH as maxLength,
158
- c.IS_NULLABLE as isNullable,
159
- c.COLUMN_DEFAULT as defaultValue,
160
- CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey,
161
- fk.REFERENCED_TABLE_SCHEMA as referencedSchema,
162
- fk.REFERENCED_TABLE_NAME as referencedTable,
163
- fk.REFERENCED_COLUMN_NAME as referencedColumn
164
- FROM INFORMATION_SCHEMA.COLUMNS c
188
+ c.column_name as "columnName",
189
+ c.data_type as "dataType",
190
+ c.character_maximum_length as "maxLength",
191
+ c.is_nullable as "isNullable",
192
+ c.column_default as "defaultValue",
193
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END as "isPrimaryKey",
194
+ fk.referenced_table_schema as "referencedSchema",
195
+ fk.referenced_table_name as "referencedTable",
196
+ fk.referenced_column_name as "referencedColumn"
197
+ FROM information_schema.columns c
165
198
  LEFT JOIN (
166
- SELECT ku.TABLE_SCHEMA, ku.TABLE_NAME, ku.COLUMN_NAME
167
- FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
168
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
169
- ON tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
170
- AND tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
171
- ) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
172
- AND c.TABLE_NAME = pk.TABLE_NAME
173
- AND c.COLUMN_NAME = pk.COLUMN_NAME
199
+ SELECT ku.table_schema, ku.table_name, ku.column_name
200
+ FROM information_schema.table_constraints tc
201
+ INNER JOIN information_schema.key_column_usage ku
202
+ ON tc.constraint_type = 'PRIMARY KEY'
203
+ AND tc.constraint_name = ku.constraint_name
204
+ ) pk ON c.table_schema = pk.table_schema
205
+ AND c.table_name = pk.table_name
206
+ AND c.column_name = pk.column_name
174
207
  LEFT JOIN (
175
208
  SELECT
176
- kcu1.TABLE_SCHEMA,
177
- kcu1.TABLE_NAME,
178
- kcu1.COLUMN_NAME,
179
- kcu2.TABLE_SCHEMA as REFERENCED_TABLE_SCHEMA,
180
- kcu2.TABLE_NAME as REFERENCED_TABLE_NAME,
181
- kcu2.COLUMN_NAME as REFERENCED_COLUMN_NAME
182
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
183
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
184
- ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
185
- AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
186
- AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
187
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
188
- ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
189
- AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
190
- AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
191
- AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
192
- ) fk ON c.TABLE_SCHEMA = fk.TABLE_SCHEMA
193
- AND c.TABLE_NAME = fk.TABLE_NAME
194
- AND c.COLUMN_NAME = fk.COLUMN_NAME
195
- WHERE c.TABLE_SCHEMA = @schema
196
- AND c.TABLE_NAME = @table
197
- ORDER BY c.ORDINAL_POSITION
209
+ kcu1.table_schema,
210
+ kcu1.table_name,
211
+ kcu1.column_name,
212
+ kcu2.table_schema as referenced_table_schema,
213
+ kcu2.table_name as referenced_table_name,
214
+ kcu2.column_name as referenced_column_name
215
+ FROM information_schema.referential_constraints rc
216
+ INNER JOIN information_schema.key_column_usage kcu1
217
+ ON rc.constraint_catalog = kcu1.constraint_catalog
218
+ AND rc.constraint_schema = kcu1.constraint_schema
219
+ AND rc.constraint_name = kcu1.constraint_name
220
+ INNER JOIN information_schema.key_column_usage kcu2
221
+ ON rc.unique_constraint_catalog = kcu2.constraint_catalog
222
+ AND rc.unique_constraint_schema = kcu2.constraint_schema
223
+ AND rc.unique_constraint_name = kcu2.constraint_name
224
+ AND kcu1.ordinal_position = kcu2.ordinal_position
225
+ ) fk ON c.table_schema = fk.table_schema
226
+ AND c.table_name = fk.table_name
227
+ AND c.column_name = fk.column_name
228
+ WHERE c.table_schema = @schema
229
+ AND c.table_name = @table
230
+ ORDER BY c.ordinal_position
198
231
  `;
199
232
  const result = await executeQuery(query, [
200
- { name: "schema", value: schema, type: sql.NVarChar },
201
- { name: "table", value: table, type: sql.NVarChar }
233
+ { name: "schema", value: schema },
234
+ { name: "table", value: table }
202
235
  ]);
203
236
  res.json(result);
204
237
  } catch (error) {
205
238
  const errorMessage = error.message || "";
206
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
207
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
239
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
240
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
208
241
  await disconnect2();
209
242
  }
210
243
  res.status(500).json({ error: error.message || "Failed to fetch table structure" });
@@ -219,62 +252,230 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
219
252
  const sortDirection = req.query.sortDirection || "asc";
220
253
  const fkDisplayMode = req.query.fkDisplayMode || "key-only";
221
254
  const offset = (page - 1) * pageSize;
222
- const filters = {};
255
+ const parsedFilters = [];
256
+ const seenFilterColumns = /* @__PURE__ */ new Set();
257
+ const parseFilterEntry = (columnName, rawValue) => {
258
+ if (!columnName || rawValue === null || rawValue === void 0 || seenFilterColumns.has(columnName)) {
259
+ return;
260
+ }
261
+ const filterValue = String(rawValue).trim();
262
+ if (!filterValue) return;
263
+ try {
264
+ const parsed = JSON.parse(filterValue);
265
+ if (parsed && typeof parsed === "object" && parsed.operator) {
266
+ parsedFilters.push({
267
+ column: columnName,
268
+ operator: parsed.operator,
269
+ value: parsed.value,
270
+ dataType: parsed.dataType
271
+ });
272
+ } else {
273
+ parsedFilters.push({
274
+ column: columnName,
275
+ operator: "contains",
276
+ value: filterValue
277
+ });
278
+ }
279
+ } catch {
280
+ parsedFilters.push({
281
+ column: columnName,
282
+ operator: "contains",
283
+ value: filterValue
284
+ });
285
+ }
286
+ seenFilterColumns.add(columnName);
287
+ };
223
288
  Object.keys(req.query).forEach((key) => {
224
289
  const match = key.match(/^filter\[(.+)\]$/);
225
- if (match && req.query[key] && String(req.query[key]).trim()) {
226
- filters[match[1]] = String(req.query[key]).trim();
290
+ if (match) {
291
+ const columnName = match[1];
292
+ parseFilterEntry(columnName, req.query[key]);
227
293
  }
228
294
  });
229
- console.log("Received filters from query:", filters);
230
- console.log("All query params:", req.query);
295
+ const nestedFilters = req.query.filter;
296
+ if (nestedFilters && typeof nestedFilters === "object" && !Array.isArray(nestedFilters)) {
297
+ Object.entries(nestedFilters).forEach(([columnName, rawValue]) => {
298
+ parseFilterEntry(columnName, rawValue);
299
+ });
300
+ }
301
+ console.log("Parsed filters:", parsedFilters);
231
302
  const pool = getConnection();
232
- if (!pool || !pool.connected) {
303
+ if (!pool) {
304
+ return res.status(400).json({ error: "Not connected to database" });
305
+ }
306
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
307
+ if (!isConnected) {
233
308
  return res.status(400).json({ error: "Not connected to database" });
234
309
  }
235
- const filterColumns = [];
236
- if (Object.keys(filters).length > 0) {
310
+ const dialect = getDialect();
311
+ const columnMetadata = {};
312
+ if (parsedFilters.length > 0) {
237
313
  try {
238
- const columnNames = Object.keys(filters);
239
- const placeholders = columnNames.map((_, i) => `@col${i}`).join(", ");
314
+ const columnNames = [...new Set(parsedFilters.map((f) => f.column))];
315
+ const placeholders = columnNames.map((_, i) => dialect.param(i + 3)).join(", ");
240
316
  const validateQuery = `
241
- SELECT COLUMN_NAME
242
- FROM INFORMATION_SCHEMA.COLUMNS
243
- WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND COLUMN_NAME IN (${placeholders})
317
+ SELECT column_name, data_type
318
+ FROM information_schema.columns
319
+ WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)} AND column_name IN (${placeholders})
244
320
  `;
245
321
  const validateParams = [
246
- { name: "schema", value: schema, type: sql.NVarChar },
247
- { name: "table", value: table, type: sql.NVarChar },
248
- ...columnNames.map((col, i) => ({ name: `col${i}`, value: col, type: sql.NVarChar }))
322
+ { name: "schema", value: schema },
323
+ { name: "table", value: table },
324
+ ...columnNames.map((col) => ({ name: "col", value: col }))
249
325
  ];
250
326
  const validateResult = await executeQuery(validateQuery, validateParams);
251
- filterColumns.push(...validateResult.map((r) => r.COLUMN_NAME));
252
- console.log("Validated filter columns:", filterColumns, "from filters:", filters);
327
+ validateResult.forEach((r) => {
328
+ const colName = r.column_name || r.COLUMN_NAME;
329
+ const dataType = r.data_type || r.DATA_TYPE;
330
+ columnMetadata[colName] = {
331
+ dataType,
332
+ exists: true
333
+ };
334
+ });
335
+ console.log("Column metadata:", columnMetadata);
253
336
  } catch (e) {
254
337
  console.error("Error validating filter columns:", e);
255
- console.error("Filters that failed validation:", filters);
256
338
  }
257
339
  }
258
340
  let whereClause = "";
259
341
  const filterParams = [];
260
- if (filterColumns.length > 0) {
261
- const validFilters = filterColumns.filter((col) => {
262
- const filterValue = filters[col];
263
- return filterValue && filterValue.trim() !== "";
342
+ let paramIndex = 1;
343
+ if (parsedFilters.length > 0) {
344
+ const whereConditions = [];
345
+ parsedFilters.forEach((filter) => {
346
+ const columnName = filter.column;
347
+ const metadata = columnMetadata[columnName];
348
+ if (!metadata || !metadata.exists) {
349
+ console.warn(`Filter column ${columnName} not found, skipping`);
350
+ return;
351
+ }
352
+ const dataType = filter.dataType || metadata.dataType;
353
+ const operator = filter.operator;
354
+ const value = filter.value;
355
+ if (value === null || value === void 0 || value === "") {
356
+ return;
357
+ }
358
+ const quotedColumn = dialect.quoteId(columnName);
359
+ try {
360
+ let condition = "";
361
+ switch (operator) {
362
+ // Text operators
363
+ case "contains":
364
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
365
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
366
+ break;
367
+ case "equals":
368
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
369
+ condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
370
+ break;
371
+ case "startsWith":
372
+ filterParams.push({ name: `filter${paramIndex}`, value: `${String(value)}%` });
373
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
374
+ break;
375
+ case "endsWith":
376
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}` });
377
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
378
+ break;
379
+ case "notContains":
380
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
381
+ condition = `${quotedColumn} NOT LIKE ${dialect.param(paramIndex)}`;
382
+ break;
383
+ // Number operators
384
+ case "eq":
385
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
386
+ condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
387
+ break;
388
+ case "gt":
389
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
390
+ condition = `${quotedColumn} > ${dialect.param(paramIndex)}`;
391
+ break;
392
+ case "gte":
393
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
394
+ condition = `${quotedColumn} >= ${dialect.param(paramIndex)}`;
395
+ break;
396
+ case "lt":
397
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
398
+ condition = `${quotedColumn} < ${dialect.param(paramIndex)}`;
399
+ break;
400
+ case "lte":
401
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
402
+ condition = `${quotedColumn} <= ${dialect.param(paramIndex)}`;
403
+ break;
404
+ case "between":
405
+ if (typeof value === "object" && "from" in value && "to" in value) {
406
+ const fromParamIndex = paramIndex;
407
+ const toParamIndex = paramIndex + 1;
408
+ filterParams.push({ name: `filter${fromParamIndex}`, value: Number(value.from) });
409
+ filterParams.push({ name: `filter${toParamIndex}`, value: Number(value.to) });
410
+ condition = `${quotedColumn} BETWEEN ${dialect.param(fromParamIndex)} AND ${dialect.param(toParamIndex)}`;
411
+ paramIndex++;
412
+ }
413
+ break;
414
+ // Date operators
415
+ case "dateEq":
416
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
417
+ condition = `${dialect.castToDate(quotedColumn)} = ${dialect.castToDate(dialect.param(paramIndex))}`;
418
+ break;
419
+ case "dateAfter":
420
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
421
+ condition = `${dialect.castToDate(quotedColumn)} > ${dialect.castToDate(dialect.param(paramIndex))}`;
422
+ break;
423
+ case "dateBefore":
424
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
425
+ condition = `${dialect.castToDate(quotedColumn)} < ${dialect.castToDate(dialect.param(paramIndex))}`;
426
+ break;
427
+ case "dateBetween":
428
+ if (typeof value === "object" && "from" in value && "to" in value) {
429
+ const fromParamIndex = paramIndex;
430
+ const toParamIndex = paramIndex + 1;
431
+ filterParams.push({ name: `filter${fromParamIndex}`, value: String(value.from) });
432
+ filterParams.push({ name: `filter${toParamIndex}`, value: String(value.to) });
433
+ condition = `${dialect.castToDate(quotedColumn)} BETWEEN ${dialect.castToDate(dialect.param(fromParamIndex))} AND ${dialect.castToDate(dialect.param(toParamIndex))}`;
434
+ paramIndex++;
435
+ }
436
+ break;
437
+ // Multiple select operators
438
+ case "in":
439
+ case "notIn":
440
+ if (Array.isArray(value) && value.length > 0) {
441
+ const placeholders = [];
442
+ value.forEach((val, i) => {
443
+ const currentIndex = paramIndex + i;
444
+ filterParams.push({ name: `filter${currentIndex}`, value: val });
445
+ placeholders.push(dialect.param(currentIndex));
446
+ });
447
+ const inOperator = operator === "in" ? "IN" : "NOT IN";
448
+ condition = `${quotedColumn} ${inOperator} (${placeholders.join(", ")})`;
449
+ paramIndex += value.length - 1;
450
+ }
451
+ break;
452
+ default:
453
+ console.warn(`Unknown filter operator: ${operator}, falling back to contains`);
454
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
455
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
456
+ }
457
+ if (condition) {
458
+ whereConditions.push(condition);
459
+ paramIndex++;
460
+ }
461
+ } catch (error) {
462
+ console.error(`Error building filter condition for ${columnName}:`, error);
463
+ }
264
464
  });
265
- if (validFilters.length > 0) {
266
- const whereConditions = validFilters.map((col, index) => {
267
- const filterValue = filters[col];
268
- filterParams.push({ name: `filter${index}`, value: `%${filterValue.trim()}%`, type: sql.NVarChar });
269
- return `[${col}] LIKE @filter${index}`;
270
- });
465
+ if (whereConditions.length > 0) {
271
466
  whereClause = `WHERE ${whereConditions.join(" AND ")}`;
272
467
  console.log("Applying WHERE clause:", whereClause);
273
- console.log("Filter params:", filterParams);
274
- console.log("Valid filters:", validFilters);
468
+ console.log("Filter params:", filterParams.map((p) => ({ name: p.name, value: p.value })));
275
469
  }
276
470
  }
277
- const countQuery = `SELECT COUNT(*) as total FROM [${schema}].[${table}]${whereClause ? " " + whereClause : ""}`;
471
+ const qualifiedDataWhereClause = parsedFilters.reduce((clause, filter) => {
472
+ const quotedColumn = dialect.quoteId(filter.column);
473
+ const quotedTableColumn = `${dialect.quoteId("t")}.${quotedColumn}`;
474
+ return clause.replace(new RegExp(quotedColumn.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), quotedTableColumn);
475
+ }, whereClause);
476
+ const quotedSchema = dialect.quoteId(schema);
477
+ const quotedTable = dialect.quoteId(table);
478
+ const countQuery = `SELECT COUNT(*) as total FROM ${quotedSchema}.${quotedTable}${whereClause ? " " + whereClause : ""}`;
278
479
  const countResult = await executeQuery(countQuery, filterParams.length > 0 ? filterParams : []);
279
480
  const total = countResult[0]?.total || 0;
280
481
  let orderByColumn = sortColumn || "";
@@ -282,17 +483,19 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
282
483
  if (orderByColumn) {
283
484
  try {
284
485
  const validateQuery = `
285
- SELECT COLUMN_NAME
286
- FROM INFORMATION_SCHEMA.COLUMNS
287
- WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND COLUMN_NAME = @column
486
+ SELECT column_name
487
+ FROM information_schema.columns
488
+ WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)} AND column_name = ${dialect.param(3)}
288
489
  `;
289
490
  const validateResult = await executeQuery(validateQuery, [
290
- { name: "schema", value: schema, type: sql.NVarChar },
291
- { name: "table", value: table, type: sql.NVarChar },
292
- { name: "column", value: orderByColumn, type: sql.NVarChar }
491
+ { name: "schema", value: schema },
492
+ { name: "table", value: table },
493
+ { name: "column", value: orderByColumn }
293
494
  ]);
294
495
  if (validateResult.length === 0) {
295
496
  orderByColumn = "";
497
+ } else {
498
+ orderByColumn = validateResult[0].column_name || validateResult[0].COLUMN_NAME || orderByColumn;
296
499
  }
297
500
  } catch (e) {
298
501
  orderByColumn = "";
@@ -300,18 +503,20 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
300
503
  }
301
504
  if (!orderByColumn) {
302
505
  try {
506
+ const topClause = dialect.topN(1);
303
507
  const structureQuery = `
304
- SELECT TOP 1 COLUMN_NAME
305
- FROM INFORMATION_SCHEMA.COLUMNS
306
- WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table
307
- ORDER BY ORDINAL_POSITION
508
+ SELECT ${topClause ? topClause + " " : ""}column_name
509
+ FROM information_schema.columns
510
+ WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)}
511
+ ORDER BY ordinal_position
512
+ ${!topClause ? dialect.limitOffset(0, 1) : ""}
308
513
  `;
309
514
  const structureResult = await executeQuery(structureQuery, [
310
- { name: "schema", value: schema, type: sql.NVarChar },
311
- { name: "table", value: table, type: sql.NVarChar }
515
+ { name: "schema", value: schema },
516
+ { name: "table", value: table }
312
517
  ]);
313
518
  if (structureResult.length > 0) {
314
- orderByColumn = structureResult[0].COLUMN_NAME;
519
+ orderByColumn = structureResult[0].column_name || structureResult[0].COLUMN_NAME;
315
520
  }
316
521
  } catch (e) {
317
522
  }
@@ -319,93 +524,97 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
319
524
  const fkJoins = [];
320
525
  const fkSelects = [];
321
526
  const fkDisplayColumns = {};
322
- if (fkDisplayMode === "key-display" || fkDisplayMode === "display-only") {
323
- const fkQuery = `
527
+ const fkQuery = `
324
528
  SELECT
325
- kcu1.COLUMN_NAME as fkColumnName,
326
- kcu2.TABLE_SCHEMA as referencedSchema,
327
- kcu2.TABLE_NAME as referencedTable,
328
- kcu2.COLUMN_NAME as referencedColumn
329
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
330
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
331
- ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
332
- AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
333
- AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
334
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
335
- ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
336
- AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
337
- AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
338
- AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
339
- WHERE kcu1.TABLE_SCHEMA = @schema
340
- AND kcu1.TABLE_NAME = @table
529
+ kcu1.column_name as "fkColumnName",
530
+ kcu2.table_schema as "referencedSchema",
531
+ kcu2.table_name as "referencedTable",
532
+ kcu2.column_name as "referencedColumn"
533
+ FROM information_schema.referential_constraints rc
534
+ INNER JOIN information_schema.key_column_usage kcu1
535
+ ON rc.constraint_catalog = kcu1.constraint_catalog
536
+ AND rc.constraint_schema = kcu1.constraint_schema
537
+ AND rc.constraint_name = kcu1.constraint_name
538
+ INNER JOIN information_schema.key_column_usage kcu2
539
+ ON rc.unique_constraint_catalog = kcu2.constraint_catalog
540
+ AND rc.unique_constraint_schema = kcu2.constraint_schema
541
+ AND rc.unique_constraint_name = kcu2.constraint_name
542
+ AND kcu1.ordinal_position = kcu2.ordinal_position
543
+ WHERE kcu1.table_schema = ${dialect.param(1)}
544
+ AND kcu1.table_name = ${dialect.param(2)}
341
545
  `;
342
- console.log("Fetching foreign keys with query:", fkQuery);
343
- const foreignKeys = await executeQuery(fkQuery, [
344
- { name: "schema", value: schema, type: sql.NVarChar },
345
- { name: "table", value: table, type: sql.NVarChar }
346
- ]);
347
- console.log(`Found ${foreignKeys.length} foreign key(s)`);
348
- if (foreignKeys.length > 0) {
349
- const uniqueRefTables = Array.from(
350
- new Set(foreignKeys.map((fk) => `${fk.referencedSchema}.${fk.referencedTable}`))
351
- );
352
- const tableConditions = uniqueRefTables.map((tableRef, idx) => {
353
- const [refSchema, refTable] = tableRef.split(".");
354
- return `(TABLE_SCHEMA = @refSchema${idx} AND TABLE_NAME = @refTable${idx})`;
355
- }).join(" OR ");
356
- const batchColumnsQuery = `
357
- SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION
358
- FROM INFORMATION_SCHEMA.COLUMNS
546
+ console.log("Fetching foreign keys with query:", fkQuery);
547
+ const foreignKeys = await executeQuery(fkQuery, [
548
+ { name: "schema", value: schema },
549
+ { name: "table", value: table }
550
+ ]);
551
+ console.log(`Found ${foreignKeys.length} foreign key(s)`);
552
+ if (foreignKeys.length > 0) {
553
+ const uniqueRefTables = Array.from(
554
+ new Set(foreignKeys.map((fk) => `${fk.referencedSchema || fk.referenced_schema}.${fk.referencedTable || fk.referenced_table}`))
555
+ );
556
+ const tableConditions = uniqueRefTables.map((tableRef, idx) => {
557
+ const [refSchema, refTable] = tableRef.split(".");
558
+ return `(table_schema = ${dialect.param(idx * 2 + 1)} AND table_name = ${dialect.param(idx * 2 + 2)})`;
559
+ }).join(" OR ");
560
+ const batchColumnsQuery = `
561
+ SELECT table_schema, table_name, column_name, data_type, ordinal_position
562
+ FROM information_schema.columns
359
563
  WHERE ${tableConditions}
360
- ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
564
+ ORDER BY table_schema, table_name, ordinal_position
361
565
  `;
362
- const batchParams = uniqueRefTables.flatMap((tableRef, idx) => {
363
- const [refSchema, refTable] = tableRef.split(".");
364
- return [
365
- { name: `refSchema${idx}`, value: refSchema, type: sql.NVarChar },
366
- { name: `refTable${idx}`, value: refTable, type: sql.NVarChar }
367
- ];
368
- });
369
- console.log("Fetching referenced table columns with query:", batchColumnsQuery);
370
- console.log("Batch parameters:", batchParams.map((p) => ({ name: p.name, value: p.value })));
371
- const allRefColumns = await executeQuery(batchColumnsQuery, batchParams);
372
- console.log(`Found columns for ${uniqueRefTables.length} referenced table(s)`);
373
- const columnsByTable = {};
374
- allRefColumns.forEach((col) => {
375
- const key = `${col.TABLE_SCHEMA}.${col.TABLE_NAME}`;
376
- if (!columnsByTable[key]) {
377
- columnsByTable[key] = [];
378
- }
379
- columnsByTable[key].push(col);
380
- });
381
- for (const fk of foreignKeys) {
382
- const fkColumn = fk.fkColumnName;
383
- const refSchema = fk.referencedSchema;
384
- const refTable = fk.referencedTable;
385
- const refColumn = fk.referencedColumn;
386
- const tableKey = `${refSchema}.${refTable}`;
387
- const refColumns = columnsByTable[tableKey] || [];
388
- const preferredNames = ["name", "title", "description", "code"];
389
- let displayColumn = null;
390
- for (const preferredName of preferredNames) {
391
- const found = refColumns.find(
392
- (col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
393
- );
394
- if (found) {
395
- displayColumn = found.COLUMN_NAME;
396
- break;
397
- }
566
+ const batchParams = uniqueRefTables.flatMap((tableRef) => {
567
+ const [refSchema, refTable] = tableRef.split(".");
568
+ return [
569
+ { name: "refSchema", value: refSchema },
570
+ { name: "refTable", value: refTable }
571
+ ];
572
+ });
573
+ console.log("Fetching referenced table columns with query:", batchColumnsQuery);
574
+ console.log("Batch parameters:", batchParams.map((p) => ({ name: p.name, value: p.value })));
575
+ const allRefColumns = await executeQuery(batchColumnsQuery, batchParams);
576
+ console.log(`Found columns for ${uniqueRefTables.length} referenced table(s)`);
577
+ const columnsByTable = {};
578
+ allRefColumns.forEach((col) => {
579
+ const schemaName = col.table_schema || col.TABLE_SCHEMA;
580
+ const tableName = col.table_name || col.TABLE_NAME;
581
+ const key = `${schemaName}.${tableName}`;
582
+ if (!columnsByTable[key]) {
583
+ columnsByTable[key] = [];
584
+ }
585
+ columnsByTable[key].push(col);
586
+ });
587
+ for (const fk of foreignKeys) {
588
+ const fkColumn = fk.fkColumnName || fk.fk_column_name;
589
+ const refSchema = fk.referencedSchema || fk.referenced_schema;
590
+ const refTable = fk.referencedTable || fk.referenced_table;
591
+ const refColumn = fk.referencedColumn || fk.referenced_column;
592
+ const tableKey = `${refSchema}.${refTable}`;
593
+ const refColumns = columnsByTable[tableKey] || [];
594
+ const preferredNames = ["name", "title", "description", "code"];
595
+ let displayColumn = null;
596
+ for (const preferredName of preferredNames) {
597
+ const found = refColumns.find((col) => {
598
+ const colName = col.column_name || col.COLUMN_NAME;
599
+ return colName.toLowerCase() === preferredName.toLowerCase();
600
+ });
601
+ if (found) {
602
+ displayColumn = found.column_name || found.COLUMN_NAME;
603
+ break;
398
604
  }
399
- if (!displayColumn) {
400
- const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
401
- const found = refColumns.find(
402
- (col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
403
- );
404
- if (found) {
405
- displayColumn = found.COLUMN_NAME;
406
- }
605
+ }
606
+ if (!displayColumn) {
607
+ const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
608
+ const found = refColumns.find((col) => {
609
+ const dataType = col.data_type || col.DATA_TYPE;
610
+ return stringTypes.some((type) => dataType.toLowerCase().includes(type));
611
+ });
612
+ if (found) {
613
+ displayColumn = found.column_name || found.COLUMN_NAME;
407
614
  }
408
- if (displayColumn) {
615
+ }
616
+ if (displayColumn) {
617
+ if (fkDisplayMode === "key-display" || fkDisplayMode === "display-only") {
409
618
  const alias = `fk_${fkColumn}`;
410
619
  fkJoins.push({
411
620
  alias,
@@ -415,89 +624,79 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
415
624
  refColumn,
416
625
  displayColumn
417
626
  });
418
- fkSelects.push(`${alias}.[${displayColumn}] as [${fkColumn}_display]`);
419
- fkDisplayColumns[fkColumn] = displayColumn;
627
+ fkSelects.push(`${dialect.quoteId(alias)}.${dialect.quoteId(displayColumn)} as ${dialect.quoteId(`${fkColumn}_display`)}`);
420
628
  }
629
+ fkDisplayColumns[fkColumn] = displayColumn;
421
630
  }
422
631
  }
423
632
  }
424
633
  const baseTableAlias = "t";
425
- let baseSelects = `[${baseTableAlias}].*`;
634
+ const quotedTableAlias = dialect.quoteId(baseTableAlias);
635
+ let baseSelects = `${quotedTableAlias}.*`;
426
636
  if (fkDisplayMode === "display-only") {
427
637
  const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
428
638
  if (fkColumnNames.length > 0) {
429
- baseSelects = `[${baseTableAlias}].*`;
639
+ baseSelects = `${quotedTableAlias}.*`;
430
640
  }
431
641
  }
432
642
  const allSelects = `${baseSelects}${fkSelects.length > 0 ? ", " + fkSelects.join(", ") : ""}`;
433
643
  const buildJoinClauses = (tableAlias) => {
434
- return fkJoins.map(
435
- (fk) => `LEFT JOIN [${fk.refSchema}].[${fk.refTable}] ${fk.alias} ON [${tableAlias}].[${fk.fkColumn}] = ${fk.alias}.[${fk.refColumn}]`
436
- ).join("\n ");
644
+ const quotedAlias = dialect.quoteId(tableAlias);
645
+ return fkJoins.map((fk) => {
646
+ const quotedRefSchema = dialect.quoteId(fk.refSchema);
647
+ const quotedRefTable = dialect.quoteId(fk.refTable);
648
+ const quotedAliasName = dialect.quoteId(fk.alias);
649
+ const quotedFkColumn = dialect.quoteId(fk.fkColumn);
650
+ const quotedRefColumn = dialect.quoteId(fk.refColumn);
651
+ return `LEFT JOIN ${quotedRefSchema}.${quotedRefTable} ${quotedAliasName} ON ${quotedAlias}.${quotedFkColumn} = ${quotedAliasName}.${quotedRefColumn}`;
652
+ }).join("\n ");
437
653
  };
438
654
  let data;
439
655
  let generatedQuery = "";
656
+ const offsetParamIndex = filterParams.length + 1;
657
+ const pageSizeParamIndex = filterParams.length + 2;
440
658
  if (orderByColumn) {
659
+ const quotedOrderByColumn = dialect.quoteId(orderByColumn);
660
+ const limitOffsetClause = dialect.limitOffset(offset, pageSize);
441
661
  const dataQuery = `
442
662
  SELECT ${allSelects}
443
- FROM [${schema}].[${table}] ${baseTableAlias}
663
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}
444
664
  ${buildJoinClauses(baseTableAlias)}
445
- ${whereClause}
446
- ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
447
- OFFSET @offset ROWS
448
- FETCH NEXT @pageSize ROWS ONLY
665
+ ${qualifiedDataWhereClause}
666
+ ORDER BY ${quotedTableAlias}.${quotedOrderByColumn} ${orderByDirection}
667
+ ${limitOffsetClause}
449
668
  `;
450
669
  generatedQuery = `SELECT ${allSelects}
451
- FROM [${schema}].[${table}] ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${whereClause ? "\n" + whereClause : ""}
452
- ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
453
- OFFSET ${offset} ROWS
454
- FETCH NEXT ${pageSize} ROWS ONLY`;
670
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${qualifiedDataWhereClause ? "\n" + qualifiedDataWhereClause : ""}
671
+ ORDER BY ${quotedTableAlias}.${quotedOrderByColumn} ${orderByDirection}
672
+ ${limitOffsetClause}`;
455
673
  console.log("Executing SQL query:", dataQuery);
456
674
  console.log("Query parameters:", {
457
675
  offset,
458
676
  pageSize,
459
677
  filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
460
678
  });
461
- data = await executeQuery(dataQuery, [
462
- { name: "offset", value: offset, type: sql.Int },
463
- { name: "pageSize", value: pageSize, type: sql.Int },
464
- ...filterParams
465
- ]);
679
+ data = await executeQuery(dataQuery, filterParams);
466
680
  } else {
467
- const innerQuery = `
468
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
469
- FROM [${schema}].[${table}]
470
- ${whereClause}
471
- `;
681
+ const dbType = getDbType();
682
+ const limitOffsetClause = dialect.limitOffset(offset, pageSize);
472
683
  const dataQuery = `
473
684
  SELECT ${allSelects}
474
- FROM (${innerQuery}) ${baseTableAlias}
685
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}
475
686
  ${buildJoinClauses(baseTableAlias)}
476
- WHERE ${baseTableAlias}.rn > @offset AND ${baseTableAlias}.rn <= @offset + @pageSize
477
- ORDER BY ${baseTableAlias}.rn
687
+ ${qualifiedDataWhereClause}
688
+ ${limitOffsetClause}
478
689
  `;
479
690
  generatedQuery = `SELECT ${allSelects}
480
- FROM (
481
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
482
- FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
483
- ) ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}
484
- WHERE ${baseTableAlias}.rn > ${offset} AND ${baseTableAlias}.rn <= ${offset + pageSize}
485
- ORDER BY ${baseTableAlias}.rn`;
691
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${qualifiedDataWhereClause ? "\n" + qualifiedDataWhereClause : ""}
692
+ ${limitOffsetClause}`;
486
693
  console.log("Executing SQL query:", dataQuery);
487
694
  console.log("Query parameters:", {
488
695
  offset,
489
696
  pageSize,
490
697
  filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
491
698
  });
492
- data = await executeQuery(dataQuery, [
493
- { name: "offset", value: offset, type: sql.Int },
494
- { name: "pageSize", value: pageSize, type: sql.Int },
495
- ...filterParams
496
- ]);
497
- data = data.map((row) => {
498
- const { rn, ...rest } = row;
499
- return rest;
500
- });
699
+ data = await executeQuery(dataQuery, filterParams);
501
700
  }
502
701
  if (fkDisplayMode === "display-only") {
503
702
  const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
@@ -514,6 +713,34 @@ ORDER BY ${baseTableAlias}.rn`;
514
713
  return filteredRow;
515
714
  });
516
715
  }
716
+ if (generatedQuery && filterParams.length > 0) {
717
+ const formatSqlLiteral = (value) => {
718
+ if (value === null || value === void 0) return "NULL";
719
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
720
+ if (typeof value === "boolean") return value ? "1" : "0";
721
+ if (value instanceof Date) return `'${value.toISOString().replace("T", " ").slice(0, 19)}'`;
722
+ return `'${String(value).replace(/'/g, "''")}'`;
723
+ };
724
+ const paramValues = new Map(
725
+ filterParams.map((p) => [p.name, p.value])
726
+ );
727
+ const dbType = getDbType();
728
+ if (dbType === "postgres") {
729
+ let paramIndex2 = 1;
730
+ generatedQuery = generatedQuery.replace(/\$(\d+)/g, (full, index) => {
731
+ const idx = parseInt(index, 10) - 1;
732
+ if (idx < filterParams.length) {
733
+ return formatSqlLiteral(filterParams[idx].value);
734
+ }
735
+ return full;
736
+ });
737
+ } else {
738
+ generatedQuery = generatedQuery.replace(/@([A-Za-z0-9_]+)/g, (full, name) => {
739
+ if (!paramValues.has(name)) return full;
740
+ return formatSqlLiteral(paramValues.get(name));
741
+ });
742
+ }
743
+ }
517
744
  res.json({
518
745
  data,
519
746
  query: generatedQuery,
@@ -529,8 +756,8 @@ ORDER BY ${baseTableAlias}.rn`;
529
756
  } catch (error) {
530
757
  console.error("Error fetching table data:", error);
531
758
  const errorMessage = error.message || "";
532
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
533
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
759
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
760
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
534
761
  await disconnect2();
535
762
  }
536
763
  const isTimeout = error.code === "ETIMEOUT" || error.code === "ESOCKET" || error.message?.includes("timeout") || error.message?.includes("ETIMEDOUT") || error.originalError?.code === "ETIMEOUT" || error.originalError?.code === "ESOCKET";
@@ -557,73 +784,68 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
557
784
  return res.status(400).json({ error: "Missing required parameters" });
558
785
  }
559
786
  const pool = getConnection();
560
- if (!pool || !pool.connected) {
787
+ if (!pool) {
788
+ return res.status(400).json({ error: "Not connected to database" });
789
+ }
790
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
791
+ if (!isConnected) {
561
792
  return res.status(400).json({ error: "Not connected to database" });
562
793
  }
794
+ const dialect = getDialect();
563
795
  const columnsQuery = `
564
- SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
565
- FROM INFORMATION_SCHEMA.COLUMNS
566
- WHERE TABLE_SCHEMA = @refSchema
567
- AND TABLE_NAME = @refTable
568
- ORDER BY ORDINAL_POSITION
796
+ SELECT column_name, data_type, character_maximum_length
797
+ FROM information_schema.columns
798
+ WHERE table_schema = ${dialect.param(1)}
799
+ AND table_name = ${dialect.param(2)}
800
+ ORDER BY ordinal_position
569
801
  `;
570
802
  const columns = await executeQuery(columnsQuery, [
571
- { name: "refSchema", value: referencedSchema, type: sql.NVarChar },
572
- { name: "refTable", value: referencedTable, type: sql.NVarChar }
803
+ { name: "refSchema", value: referencedSchema },
804
+ { name: "refTable", value: referencedTable }
573
805
  ]);
574
- const referencedColInfo = columns.find(
575
- (col) => col.COLUMN_NAME === referencedColumn
576
- );
806
+ const referencedColInfo = columns.find((col) => {
807
+ const colName = col.column_name || col.COLUMN_NAME;
808
+ return colName === referencedColumn;
809
+ });
577
810
  if (!referencedColInfo) {
578
811
  return res.status(400).json({ error: `Referenced column '${referencedColumn}' not found` });
579
812
  }
580
- const getSqlType = (dataType) => {
581
- const dt = dataType.toLowerCase();
582
- if (dt === "int" || dt === "integer") return sql.Int;
583
- if (dt === "bigint") return sql.BigInt;
584
- if (dt === "smallint") return sql.SmallInt;
585
- if (dt === "tinyint") return sql.TinyInt;
586
- if (dt === "bit") return sql.Bit;
587
- if (dt === "float" || dt === "real" || dt === "double precision") return sql.Float;
588
- if (dt === "decimal" || dt === "numeric" || dt === "money" || dt === "smallmoney") return sql.Decimal(18, 0);
589
- if (dt === "datetime" || dt === "datetime2" || dt === "smalldatetime") return sql.DateTime;
590
- if (dt === "date") return sql.Date;
591
- if (dt === "time") return sql.Time;
592
- if (dt === "uniqueidentifier") return sql.UniqueIdentifier;
593
- return sql.NVarChar;
594
- };
595
- const referencedColumnType = getSqlType(referencedColInfo.DATA_TYPE);
596
813
  const preferredNames = ["name", "title", "description", "code"];
597
814
  let displayColumn = null;
598
815
  for (const preferredName of preferredNames) {
599
- const found = columns.find(
600
- (col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
601
- );
816
+ const found = columns.find((col) => {
817
+ const colName = col.column_name || col.COLUMN_NAME;
818
+ return colName.toLowerCase() === preferredName.toLowerCase();
819
+ });
602
820
  if (found) {
603
- displayColumn = found.COLUMN_NAME;
821
+ displayColumn = found.column_name || found.COLUMN_NAME;
604
822
  break;
605
823
  }
606
824
  }
607
825
  if (!displayColumn) {
608
826
  const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
609
- const found = columns.find(
610
- (col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
611
- );
827
+ const found = columns.find((col) => {
828
+ const dataType = col.data_type || col.DATA_TYPE;
829
+ return stringTypes.some((type) => dataType.toLowerCase().includes(type));
830
+ });
612
831
  if (found) {
613
- displayColumn = found.COLUMN_NAME;
832
+ displayColumn = found.column_name || found.COLUMN_NAME;
614
833
  }
615
834
  }
616
- const placeholders = ids.map((_, i) => `@id${i}`).join(", ");
617
- const selectColumns = displayColumn ? `[${referencedColumn}], [${displayColumn}]` : `[${referencedColumn}]`;
835
+ const quotedRefSchema = dialect.quoteId(referencedSchema);
836
+ const quotedRefTable = dialect.quoteId(referencedTable);
837
+ const quotedRefColumn = dialect.quoteId(referencedColumn);
838
+ const quotedDisplayColumn = displayColumn ? dialect.quoteId(displayColumn) : null;
839
+ const placeholders = ids.map((_, i) => dialect.param(i + 1)).join(", ");
840
+ const selectColumns = displayColumn ? `${quotedRefColumn}, ${quotedDisplayColumn}` : quotedRefColumn;
618
841
  const dataQuery = `
619
842
  SELECT ${selectColumns}
620
- FROM [${referencedSchema}].[${referencedTable}]
621
- WHERE [${referencedColumn}] IN (${placeholders})
843
+ FROM ${quotedRefSchema}.${quotedRefTable}
844
+ WHERE ${quotedRefColumn} IN (${placeholders})
622
845
  `;
623
- const params = ids.map((id, i) => ({
624
- name: `id${i}`,
625
- value: id,
626
- type: referencedColumnType
846
+ const params = ids.map((id) => ({
847
+ name: "id",
848
+ value: id
627
849
  }));
628
850
  const result = await executeQuery(dataQuery, params);
629
851
  const dataMap = {};
@@ -635,37 +857,223 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
635
857
  } catch (error) {
636
858
  console.error("Error fetching related data:", error);
637
859
  const errorMessage = error.message || "";
638
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
639
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
860
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
861
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
640
862
  await disconnect2();
641
863
  }
642
864
  res.status(500).json({ error: error.message || "Failed to fetch related data" });
643
865
  }
644
866
  });
867
+ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
868
+ try {
869
+ const { schema, table, column } = req.params;
870
+ const searchQuery = req.query.search;
871
+ const columnsParam = req.query.columns;
872
+ const pool = getConnection();
873
+ if (!pool) {
874
+ return res.status(400).json({ error: "Not connected to database" });
875
+ }
876
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
877
+ if (!isConnected) {
878
+ return res.status(400).json({ error: "Not connected to database" });
879
+ }
880
+ const dialect = getDialect();
881
+ const columnQuery = `
882
+ SELECT
883
+ c.column_name,
884
+ c.data_type,
885
+ fk.referenced_table_schema as "referencedSchema",
886
+ fk.referenced_table_name as "referencedTable",
887
+ fk.referenced_column_name as "referencedColumn"
888
+ FROM information_schema.columns c
889
+ LEFT JOIN (
890
+ SELECT
891
+ kcu1.table_schema,
892
+ kcu1.table_name,
893
+ kcu1.column_name,
894
+ kcu2.table_schema as referenced_table_schema,
895
+ kcu2.table_name as referenced_table_name,
896
+ kcu2.column_name as referenced_column_name
897
+ FROM information_schema.referential_constraints rc
898
+ INNER JOIN information_schema.key_column_usage kcu1
899
+ ON rc.constraint_catalog = kcu1.constraint_catalog
900
+ AND rc.constraint_schema = kcu1.constraint_schema
901
+ AND rc.constraint_name = kcu1.constraint_name
902
+ INNER JOIN information_schema.key_column_usage kcu2
903
+ ON rc.unique_constraint_catalog = kcu2.constraint_catalog
904
+ AND rc.unique_constraint_schema = kcu2.constraint_schema
905
+ AND rc.unique_constraint_name = kcu2.constraint_name
906
+ AND kcu1.ordinal_position = kcu2.ordinal_position
907
+ ) fk ON c.table_schema = fk.table_schema
908
+ AND c.table_name = fk.table_name
909
+ AND c.column_name = fk.column_name
910
+ WHERE c.table_schema = ${dialect.param(1)} AND c.table_name = ${dialect.param(2)} AND c.column_name = ${dialect.param(3)}
911
+ `;
912
+ const columnResult = await executeQuery(columnQuery, [
913
+ { name: "schema", value: schema },
914
+ { name: "table", value: table },
915
+ { name: "column", value: column }
916
+ ]);
917
+ if (columnResult.length === 0) {
918
+ return res.status(400).json({ error: "Column not found" });
919
+ }
920
+ const columnInfo = columnResult[0];
921
+ const isForeignKey = !!(columnInfo.referencedSchema || columnInfo.referenced_schema) && !!(columnInfo.referencedTable || columnInfo.referenced_table);
922
+ let query = "";
923
+ const params = [];
924
+ let displayColumn = null;
925
+ if (isForeignKey) {
926
+ const refSchema = columnInfo.referencedSchema || columnInfo.referenced_schema;
927
+ const refTable = columnInfo.referencedTable || columnInfo.referenced_table;
928
+ const refColumn = columnInfo.referencedColumn || columnInfo.referenced_column;
929
+ const quotedRefSchema = dialect.quoteId(refSchema);
930
+ const quotedRefTable = dialect.quoteId(refTable);
931
+ const quotedRefColumn = dialect.quoteId(refColumn);
932
+ let columnsToSelect = [];
933
+ if (columnsParam) {
934
+ columnsToSelect = columnsParam.split(",").map((c) => c.trim()).filter((c) => c);
935
+ }
936
+ if (columnsToSelect.length > 0) {
937
+ const selectCols = columnsToSelect.map((col) => dialect.quoteId(col)).join(", ");
938
+ query = `SELECT DISTINCT ${selectCols} FROM ${quotedRefSchema}.${quotedRefTable}`;
939
+ if (searchQuery && searchQuery.trim()) {
940
+ const searchCols = columnsToSelect.map((col) => `${dialect.quoteId(col)} LIKE ${dialect.param(1)}`).join(" OR ");
941
+ query += ` WHERE (${searchCols}) AND ${quotedRefColumn} IS NOT NULL`;
942
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
943
+ } else {
944
+ query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
945
+ }
946
+ const orderByCol = columnsToSelect.length > 1 ? columnsToSelect[1] : columnsToSelect[0];
947
+ query += ` ORDER BY ${dialect.quoteId(orderByCol)} ${dialect.limitOffset(0, 1e3)}`;
948
+ } else {
949
+ const refColumnsQuery = `
950
+ SELECT column_name, data_type
951
+ FROM information_schema.columns
952
+ WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)}
953
+ ORDER BY ordinal_position
954
+ `;
955
+ const refColumns = await executeQuery(refColumnsQuery, [
956
+ { name: "refSchema", value: refSchema },
957
+ { name: "refTable", value: refTable }
958
+ ]);
959
+ const preferredNames = ["name", "title", "description", "code"];
960
+ for (const preferredName of preferredNames) {
961
+ const found = refColumns.find((col) => {
962
+ const colName = col.column_name || col.COLUMN_NAME;
963
+ return colName.toLowerCase() === preferredName.toLowerCase() && colName.toLowerCase() !== refColumn.toLowerCase();
964
+ });
965
+ if (found) {
966
+ displayColumn = found.column_name || found.COLUMN_NAME;
967
+ break;
968
+ }
969
+ }
970
+ if (!displayColumn) {
971
+ const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
972
+ const found = refColumns.find((col) => {
973
+ const colName = col.column_name || col.COLUMN_NAME;
974
+ const dataType = col.data_type || col.DATA_TYPE;
975
+ return colName.toLowerCase() !== refColumn.toLowerCase() && stringTypes.some((type) => dataType.toLowerCase().includes(type));
976
+ });
977
+ if (found) {
978
+ displayColumn = found.column_name || found.COLUMN_NAME;
979
+ }
980
+ }
981
+ const quotedDisplayColumn = displayColumn ? dialect.quoteId(displayColumn) : null;
982
+ const selectCols = displayColumn ? `${quotedRefColumn}, ${quotedDisplayColumn}` : quotedRefColumn;
983
+ query = `SELECT DISTINCT ${selectCols} FROM ${quotedRefSchema}.${quotedRefTable}`;
984
+ if (searchQuery && searchQuery.trim()) {
985
+ if (displayColumn) {
986
+ query += ` WHERE ${quotedDisplayColumn} LIKE ${dialect.param(1)} OR ${quotedRefColumn} LIKE ${dialect.param(1)}`;
987
+ } else {
988
+ query += ` WHERE ${quotedRefColumn} LIKE ${dialect.param(1)}`;
989
+ }
990
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
991
+ query += ` AND ${quotedRefColumn} IS NOT NULL`;
992
+ } else {
993
+ query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
994
+ }
995
+ query += ` ORDER BY ${displayColumn ? quotedDisplayColumn : quotedRefColumn} ${dialect.limitOffset(0, 1e3)}`;
996
+ }
997
+ } else {
998
+ const dataType = (columnInfo.data_type || columnInfo.DATA_TYPE || "").toLowerCase();
999
+ const quotedSchema = dialect.quoteId(schema);
1000
+ const quotedTable = dialect.quoteId(table);
1001
+ const quotedColumn = dialect.quoteId(column);
1002
+ const columnsToSelect = columnsParam ? columnsParam.split(",").map((c) => c.trim()).filter((c) => c) : [];
1003
+ if (columnsToSelect.length > 0) {
1004
+ const quotedColumns = columnsToSelect.map((col) => dialect.quoteId(col));
1005
+ const keyColumn = quotedColumns[0];
1006
+ const orderByColumn = quotedColumns.length > 1 ? quotedColumns[1] : quotedColumns[0];
1007
+ query = `SELECT DISTINCT ${quotedColumns.join(", ")} FROM ${quotedSchema}.${quotedTable}`;
1008
+ if (searchQuery && searchQuery.trim()) {
1009
+ const searchCols = quotedColumns.map((col) => `${dialect.tryCastToNVarChar(col)} LIKE ${dialect.param(1)}`).join(" OR ");
1010
+ query += ` WHERE (${searchCols}) AND ${keyColumn} IS NOT NULL`;
1011
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1012
+ } else {
1013
+ query += ` WHERE ${keyColumn} IS NOT NULL`;
1014
+ }
1015
+ query += ` ORDER BY ${orderByColumn} ${dialect.limitOffset(0, 1e3)}`;
1016
+ } else {
1017
+ query = `SELECT DISTINCT ${quotedColumn} FROM ${quotedSchema}.${quotedTable}`;
1018
+ if (searchQuery && searchQuery.trim()) {
1019
+ if (["varchar", "nvarchar", "char", "nchar", "text", "ntext"].some((t) => dataType.includes(t))) {
1020
+ query += ` WHERE ${quotedColumn} LIKE ${dialect.param(1)}`;
1021
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1022
+ query += ` AND ${quotedColumn} IS NOT NULL`;
1023
+ } else {
1024
+ query += ` WHERE ${quotedColumn} IS NOT NULL`;
1025
+ }
1026
+ } else {
1027
+ query += ` WHERE ${quotedColumn} IS NOT NULL`;
1028
+ }
1029
+ query += ` ORDER BY ${quotedColumn} ${dialect.limitOffset(0, 1e3)}`;
1030
+ }
1031
+ }
1032
+ const result = await executeQuery(query, params);
1033
+ res.json(result);
1034
+ } catch (error) {
1035
+ console.error("Error fetching distinct values:", error);
1036
+ const errorMessage = error.message || "";
1037
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
1038
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
1039
+ await disconnect2();
1040
+ }
1041
+ res.status(500).json({ error: error.message || "Failed to fetch distinct values" });
1042
+ }
1043
+ });
645
1044
 
646
1045
  // src/server/routes/query.ts
647
1046
  import { Router as Router3 } from "express";
648
1047
  var queryRoutes = Router3();
649
1048
  queryRoutes.post("/", async (req, res) => {
650
1049
  try {
651
- const { query: sqlQuery } = req.body;
1050
+ const { query: sqlQuery, queryId } = req.body;
652
1051
  if (!sqlQuery || typeof sqlQuery !== "string") {
653
1052
  return res.status(400).json({ error: "Query is required" });
654
1053
  }
1054
+ const activeQueryId = queryId || generateQueryId();
655
1055
  const startTime = Date.now();
656
- const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery);
1056
+ const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery, void 0, activeQueryId);
657
1057
  const executionTime = Date.now() - startTime;
658
1058
  res.json({
659
1059
  data: resultSets[0] || [],
660
1060
  resultSets: resultSets.length > 0 ? resultSets : [],
661
1061
  executionTime,
662
- columnMetadata
1062
+ columnMetadata,
663
1063
  // Include column metadata for empty result sets
1064
+ queryId: activeQueryId
1065
+ // Return queryId so client can track it
664
1066
  });
665
1067
  } catch (error) {
1068
+ if (error.cancelled || error.code === "ECANCEL") {
1069
+ return res.status(499).json({
1070
+ error: "Query was cancelled",
1071
+ cancelled: true
1072
+ });
1073
+ }
666
1074
  const errorMessage = error.message || "";
667
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
668
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
1075
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
1076
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
669
1077
  await disconnect2();
670
1078
  }
671
1079
  res.status(500).json({
@@ -674,6 +1082,24 @@ queryRoutes.post("/", async (req, res) => {
674
1082
  });
675
1083
  }
676
1084
  });
1085
+ queryRoutes.post("/cancel", async (req, res) => {
1086
+ try {
1087
+ const { queryId } = req.body;
1088
+ if (!queryId || typeof queryId !== "string") {
1089
+ return res.status(400).json({ error: "Query ID is required" });
1090
+ }
1091
+ const cancelled = cancelQuery(queryId);
1092
+ if (cancelled) {
1093
+ res.json({ success: true, message: "Query cancellation requested" });
1094
+ } else {
1095
+ res.status(404).json({ error: "Query not found or already completed" });
1096
+ }
1097
+ } catch (error) {
1098
+ res.status(500).json({
1099
+ error: error.message || "Failed to cancel query"
1100
+ });
1101
+ }
1102
+ });
677
1103
 
678
1104
  // src/server/index.ts
679
1105
  var __filename = fileURLToPath(import.meta.url);
@@ -734,7 +1160,7 @@ function getProvidedConnectionString() {
734
1160
  // src/cli/index.ts
735
1161
  import open from "open";
736
1162
  var program = new Command();
737
- program.name("datapeek").description("A local SQL database browser").version("0.1.0").argument("[connectionString]", "SQL Server connection string").option("-p, --port <port>", "Port to run the server on", "4983").option("--no-open", "Do not open browser automatically").action(async (connectionString, options) => {
1163
+ program.name("datapeek").description("A local SQL database browser").version("0.1.0").argument("[connectionString]", "Database connection string (SQL Server or PostgreSQL)").option("-p, --port <port>", "Port to run the server on", "4983").option("--no-open", "Do not open browser automatically").action(async (connectionString, options) => {
738
1164
  const port = parseInt(options?.port || "4983", 10);
739
1165
  try {
740
1166
  const server = await startServer(port, connectionString);
@@ -742,7 +1168,8 @@ program.name("datapeek").description("A local SQL database browser").version("0.
742
1168
  console.log(`
743
1169
  \u{1F680} Datapeek server running at ${url}`);
744
1170
  if (connectionString) {
745
- console.log(`\u{1F4CA} Connection string provided: ${connectionString.substring(0, 20)}...`);
1171
+ const dbType = connectionString.trim().startsWith("postgresql://") || connectionString.trim().startsWith("postgres://") ? "PostgreSQL" : "SQL Server";
1172
+ console.log(`\u{1F4CA} Connection string provided (${dbType}): ${connectionString.substring(0, 20)}...`);
746
1173
  }
747
1174
  console.log(`
748
1175
  Press Ctrl+C to stop