datapeek 0.1.11 → 0.1.13

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 (97) hide show
  1. package/README.md +38 -9
  2. package/dist/cli/chunk-2I4MNNVK.js +628 -0
  3. package/dist/cli/chunk-4GDQ7VJZ.js +662 -0
  4. package/dist/cli/chunk-4MIQHH6K.js +47 -0
  5. package/dist/cli/chunk-6K3WSEIM.js +375 -0
  6. package/dist/cli/chunk-6PMCDR7J.js +628 -0
  7. package/dist/cli/chunk-B7K46745.js +605 -0
  8. package/dist/cli/chunk-CK2O76SG.js +713 -0
  9. package/dist/cli/chunk-FXOMCTFV.js +696 -0
  10. package/dist/cli/chunk-G4ID5SFT.js +628 -0
  11. package/dist/cli/chunk-GCVFEHSZ.js +708 -0
  12. package/dist/cli/chunk-LGE2JQ3S.js +628 -0
  13. package/dist/cli/chunk-NDLKLB5A.js +710 -0
  14. package/dist/cli/chunk-PG6I26XT.js +614 -0
  15. package/dist/cli/chunk-RK2Q2AX5.js +582 -0
  16. package/dist/cli/chunk-S5LRCCK6.js +582 -0
  17. package/dist/cli/chunk-UBFQXPPF.js +701 -0
  18. package/dist/cli/chunk-X2DQBOP6.js +712 -0
  19. package/dist/cli/chunk-XRMPVZIS.js +709 -0
  20. package/dist/cli/chunk-Z5G3UEXN.js +711 -0
  21. package/dist/cli/chunk-ZMP7S3G6.js +696 -0
  22. package/dist/cli/db-32TDDTFP.js +28 -0
  23. package/dist/cli/db-47O74DN5.js +34 -0
  24. package/dist/cli/db-4U22OWGB.js +29 -0
  25. package/dist/cli/db-CL72XDNO.js +34 -0
  26. package/dist/cli/db-CZCZOEID.js +28 -0
  27. package/dist/cli/db-IX3DXZ2C.js +34 -0
  28. package/dist/cli/db-LJCRTZTY.js +34 -0
  29. package/dist/cli/db-LNX4UMRF.js +28 -0
  30. package/dist/cli/db-NESFXDWL.js +28 -0
  31. package/dist/cli/db-OIJ2PJK6.js +34 -0
  32. package/dist/cli/db-OPAF6ZO3.js +28 -0
  33. package/dist/cli/db-PQURQHSK.js +28 -0
  34. package/dist/cli/db-QN5MTDYB.js +28 -0
  35. package/dist/cli/db-RBMUR3OK.js +34 -0
  36. package/dist/cli/db-REMUYLPP.js +34 -0
  37. package/dist/cli/db-RTHQ4ZAU.js +28 -0
  38. package/dist/cli/db-SHFLGGWD.js +28 -0
  39. package/dist/cli/db-ULMGZXQ5.js +34 -0
  40. package/dist/cli/db-YOGJ4LXE.js +28 -0
  41. package/dist/cli/dev.js +449 -374
  42. package/dist/cli/index.js +452 -376
  43. package/dist/cli/queryCancellation-4CMKYTFU.js +46 -0
  44. package/dist/cli/queryCancellation-E5O4FBUY.js +14 -0
  45. package/dist/client/assets/index-H7ji6dtA.js +385 -0
  46. package/dist/client/assets/index-TFw4Z1Ky.css +1 -0
  47. package/dist/client/index.html +2 -2
  48. package/dist/client/screenshots/dark1.png +0 -0
  49. package/dist/client/screenshots/dark2.png +0 -0
  50. package/dist/client/screenshots/light1.png +0 -0
  51. package/dist/client/screenshots/light2.png +0 -0
  52. package/dist/server/chunk-26GSUAI4.js +710 -0
  53. package/dist/server/chunk-36OOXHKI.js +659 -0
  54. package/dist/server/chunk-3DAIYH42.js +44 -0
  55. package/dist/server/chunk-4ELMZYWA.js +611 -0
  56. package/dist/server/chunk-5ESS5YBD.js +707 -0
  57. package/dist/server/chunk-7HIBWXBL.js +625 -0
  58. package/dist/server/chunk-BH22Q2FV.js +705 -0
  59. package/dist/server/chunk-CFU2CLKS.js +693 -0
  60. package/dist/server/chunk-CXA3HXTK.js +698 -0
  61. package/dist/server/chunk-DAUUXYQF.js +706 -0
  62. package/dist/server/chunk-FMSIEQ2T.js +579 -0
  63. package/dist/server/chunk-G6A463RM.js +625 -0
  64. package/dist/server/chunk-H6DUOXOK.js +625 -0
  65. package/dist/server/chunk-JCRZ6H7P.js +579 -0
  66. package/dist/server/chunk-L6LL3NE7.js +602 -0
  67. package/dist/server/chunk-TH6TUILG.js +709 -0
  68. package/dist/server/chunk-UJ2LRK25.js +373 -0
  69. package/dist/server/chunk-YI67PW64.js +625 -0
  70. package/dist/server/chunk-ZDZ2C5ZQ.js +693 -0
  71. package/dist/server/chunk-ZJOVVCPT.js +708 -0
  72. package/dist/server/db-22DQ6YG7.js +32 -0
  73. package/dist/server/db-3CBO6RHU.js +26 -0
  74. package/dist/server/db-3XN2KWYT.js +32 -0
  75. package/dist/server/db-3Y6K4EDP.js +26 -0
  76. package/dist/server/db-57HXNWPD.js +26 -0
  77. package/dist/server/db-5GCTDKYH.js +27 -0
  78. package/dist/server/db-5VQOTJ52.js +26 -0
  79. package/dist/server/db-B5L7VQ7E.js +32 -0
  80. package/dist/server/db-CJPCGHL3.js +32 -0
  81. package/dist/server/db-DAQ4BVCU.js +32 -0
  82. package/dist/server/db-EEA6EWHL.js +26 -0
  83. package/dist/server/db-KR7Z5M5V.js +32 -0
  84. package/dist/server/db-LMSBISDM.js +26 -0
  85. package/dist/server/db-LSOQE7N7.js +32 -0
  86. package/dist/server/db-NHBP6YLH.js +26 -0
  87. package/dist/server/db-OWVJVBBD.js +26 -0
  88. package/dist/server/db-TTVXKX7P.js +26 -0
  89. package/dist/server/db-YJ753EQV.js +26 -0
  90. package/dist/server/db-ZV6GZHFU.js +32 -0
  91. package/dist/server/dev.js +449 -374
  92. package/dist/server/index.js +449 -374
  93. package/dist/server/queryCancellation-M56DISJX.js +43 -0
  94. package/dist/server/queryCancellation-T2HSNMIV.js +12 -0
  95. package/package.json +3 -1
  96. package/dist/client/assets/index-CPoX5Biw.css +0 -1
  97. package/dist/client/assets/index-KtK9Gzwi.js +0 -375
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) {
125
146
  return res.status(400).json({ error: "Not connected to database" });
126
147
  }
148
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
149
+ if (!isConnected) {
150
+ return res.status(400).json({ error: "Not connected to database" });
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) {
151
179
  return res.status(400).json({ error: "Not connected to database" });
152
180
  }
181
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
182
+ if (!isConnected) {
183
+ return res.status(400).json({ error: "Not connected to database" });
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" });
@@ -267,28 +300,35 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
267
300
  }
268
301
  console.log("Parsed filters:", parsedFilters);
269
302
  const pool = getConnection();
270
- 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) {
271
308
  return res.status(400).json({ error: "Not connected to database" });
272
309
  }
310
+ const dialect = getDialect();
273
311
  const columnMetadata = {};
274
312
  if (parsedFilters.length > 0) {
275
313
  try {
276
314
  const columnNames = [...new Set(parsedFilters.map((f) => f.column))];
277
- const placeholders = columnNames.map((_, i) => `@col${i}`).join(", ");
315
+ const placeholders = columnNames.map((_, i) => dialect.param(i + 3)).join(", ");
278
316
  const validateQuery = `
279
- SELECT COLUMN_NAME, DATA_TYPE
280
- FROM INFORMATION_SCHEMA.COLUMNS
281
- 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})
282
320
  `;
283
321
  const validateParams = [
284
- { name: "schema", value: schema, type: sql.NVarChar },
285
- { name: "table", value: table, type: sql.NVarChar },
286
- ...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 }))
287
325
  ];
288
326
  const validateResult = await executeQuery(validateQuery, validateParams);
289
327
  validateResult.forEach((r) => {
290
- columnMetadata[r.COLUMN_NAME] = {
291
- dataType: r.DATA_TYPE,
328
+ const colName = r.column_name || r.COLUMN_NAME;
329
+ const dataType = r.data_type || r.DATA_TYPE;
330
+ columnMetadata[colName] = {
331
+ dataType,
292
332
  exists: true
293
333
  };
294
334
  });
@@ -297,24 +337,9 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
297
337
  console.error("Error validating filter columns:", e);
298
338
  }
299
339
  }
300
- const getSqlType = (dataType) => {
301
- const dt = dataType.toLowerCase();
302
- if (dt === "int" || dt === "integer") return sql.Int;
303
- if (dt === "bigint") return sql.BigInt;
304
- if (dt === "smallint") return sql.SmallInt;
305
- if (dt === "tinyint") return sql.TinyInt;
306
- if (dt === "bit") return sql.Bit;
307
- if (dt === "float" || dt === "real" || dt === "double precision") return sql.Float;
308
- if (dt === "decimal" || dt === "numeric" || dt === "money" || dt === "smallmoney") return sql.Decimal(18, 2);
309
- if (dt === "datetime" || dt === "datetime2" || dt === "smalldatetime") return sql.DateTime;
310
- if (dt === "date") return sql.Date;
311
- if (dt === "time") return sql.Time;
312
- if (dt === "uniqueidentifier") return sql.UniqueIdentifier;
313
- return sql.NVarChar;
314
- };
315
340
  let whereClause = "";
316
341
  const filterParams = [];
317
- let paramIndex = 0;
342
+ let paramIndex = 1;
318
343
  if (parsedFilters.length > 0) {
319
344
  const whereConditions = [];
320
345
  parsedFilters.forEach((filter) => {
@@ -330,83 +355,82 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
330
355
  if (value === null || value === void 0 || value === "") {
331
356
  return;
332
357
  }
333
- const sqlType = getSqlType(dataType);
334
- const paramName = `filter${paramIndex}`;
358
+ const quotedColumn = dialect.quoteId(columnName);
335
359
  try {
336
360
  let condition = "";
337
361
  switch (operator) {
338
362
  // Text operators
339
363
  case "contains":
340
- filterParams.push({ name: paramName, value: `%${String(value)}%`, type: sql.NVarChar });
341
- condition = `[${columnName}] LIKE @${paramName}`;
364
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
365
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
342
366
  break;
343
367
  case "equals":
344
- filterParams.push({ name: paramName, value: String(value), type: sqlType });
345
- condition = `[${columnName}] = @${paramName}`;
368
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
369
+ condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
346
370
  break;
347
371
  case "startsWith":
348
- filterParams.push({ name: paramName, value: `${String(value)}%`, type: sql.NVarChar });
349
- condition = `[${columnName}] LIKE @${paramName}`;
372
+ filterParams.push({ name: `filter${paramIndex}`, value: `${String(value)}%` });
373
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
350
374
  break;
351
375
  case "endsWith":
352
- filterParams.push({ name: paramName, value: `%${String(value)}`, type: sql.NVarChar });
353
- condition = `[${columnName}] LIKE @${paramName}`;
376
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}` });
377
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
354
378
  break;
355
379
  case "notContains":
356
- filterParams.push({ name: paramName, value: `%${String(value)}%`, type: sql.NVarChar });
357
- condition = `[${columnName}] NOT LIKE @${paramName}`;
380
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
381
+ condition = `${quotedColumn} NOT LIKE ${dialect.param(paramIndex)}`;
358
382
  break;
359
383
  // Number operators
360
384
  case "eq":
361
- filterParams.push({ name: paramName, value: Number(value), type: sqlType });
362
- condition = `[${columnName}] = @${paramName}`;
385
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
386
+ condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
363
387
  break;
364
388
  case "gt":
365
- filterParams.push({ name: paramName, value: Number(value), type: sqlType });
366
- condition = `[${columnName}] > @${paramName}`;
389
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
390
+ condition = `${quotedColumn} > ${dialect.param(paramIndex)}`;
367
391
  break;
368
392
  case "gte":
369
- filterParams.push({ name: paramName, value: Number(value), type: sqlType });
370
- condition = `[${columnName}] >= @${paramName}`;
393
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
394
+ condition = `${quotedColumn} >= ${dialect.param(paramIndex)}`;
371
395
  break;
372
396
  case "lt":
373
- filterParams.push({ name: paramName, value: Number(value), type: sqlType });
374
- condition = `[${columnName}] < @${paramName}`;
397
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
398
+ condition = `${quotedColumn} < ${dialect.param(paramIndex)}`;
375
399
  break;
376
400
  case "lte":
377
- filterParams.push({ name: paramName, value: Number(value), type: sqlType });
378
- condition = `[${columnName}] <= @${paramName}`;
401
+ filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
402
+ condition = `${quotedColumn} <= ${dialect.param(paramIndex)}`;
379
403
  break;
380
404
  case "between":
381
405
  if (typeof value === "object" && "from" in value && "to" in value) {
382
- const fromParam = `filter${paramIndex}`;
383
- const toParam = `filter${paramIndex + 1}`;
384
- filterParams.push({ name: fromParam, value: Number(value.from), type: sqlType });
385
- filterParams.push({ name: toParam, value: Number(value.to), type: sqlType });
386
- condition = `[${columnName}] BETWEEN @${fromParam} AND @${toParam}`;
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)}`;
387
411
  paramIndex++;
388
412
  }
389
413
  break;
390
414
  // Date operators
391
415
  case "dateEq":
392
- filterParams.push({ name: paramName, value: String(value), type: sqlType });
393
- condition = `CAST([${columnName}] AS DATE) = CAST(@${paramName} AS DATE)`;
416
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
417
+ condition = `${dialect.castToDate(quotedColumn)} = ${dialect.castToDate(dialect.param(paramIndex))}`;
394
418
  break;
395
419
  case "dateAfter":
396
- filterParams.push({ name: paramName, value: String(value), type: sqlType });
397
- condition = `CAST([${columnName}] AS DATE) > CAST(@${paramName} AS DATE)`;
420
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
421
+ condition = `${dialect.castToDate(quotedColumn)} > ${dialect.castToDate(dialect.param(paramIndex))}`;
398
422
  break;
399
423
  case "dateBefore":
400
- filterParams.push({ name: paramName, value: String(value), type: sqlType });
401
- condition = `CAST([${columnName}] AS DATE) < CAST(@${paramName} AS DATE)`;
424
+ filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
425
+ condition = `${dialect.castToDate(quotedColumn)} < ${dialect.castToDate(dialect.param(paramIndex))}`;
402
426
  break;
403
427
  case "dateBetween":
404
428
  if (typeof value === "object" && "from" in value && "to" in value) {
405
- const fromParam = `filter${paramIndex}`;
406
- const toParam = `filter${paramIndex + 1}`;
407
- filterParams.push({ name: fromParam, value: String(value.from), type: sqlType });
408
- filterParams.push({ name: toParam, value: String(value.to), type: sqlType });
409
- condition = `CAST([${columnName}] AS DATE) BETWEEN CAST(@${fromParam} AS DATE) AND CAST(@${toParam} AS DATE)`;
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))}`;
410
434
  paramIndex++;
411
435
  }
412
436
  break;
@@ -414,19 +438,21 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
414
438
  case "in":
415
439
  case "notIn":
416
440
  if (Array.isArray(value) && value.length > 0) {
417
- const placeholders = value.map((_, i) => `@${paramName}_${i}`).join(", ");
441
+ const placeholders = [];
418
442
  value.forEach((val, i) => {
419
- filterParams.push({ name: `${paramName}_${i}`, value: val, type: sqlType });
443
+ const currentIndex = paramIndex + i;
444
+ filterParams.push({ name: `filter${currentIndex}`, value: val });
445
+ placeholders.push(dialect.param(currentIndex));
420
446
  });
421
447
  const inOperator = operator === "in" ? "IN" : "NOT IN";
422
- condition = `[${columnName}] ${inOperator} (${placeholders})`;
448
+ condition = `${quotedColumn} ${inOperator} (${placeholders.join(", ")})`;
423
449
  paramIndex += value.length - 1;
424
450
  }
425
451
  break;
426
452
  default:
427
453
  console.warn(`Unknown filter operator: ${operator}, falling back to contains`);
428
- filterParams.push({ name: paramName, value: `%${String(value)}%`, type: sql.NVarChar });
429
- condition = `[${columnName}] LIKE @${paramName}`;
454
+ filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
455
+ condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
430
456
  }
431
457
  if (condition) {
432
458
  whereConditions.push(condition);
@@ -443,11 +469,13 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
443
469
  }
444
470
  }
445
471
  const qualifiedDataWhereClause = parsedFilters.reduce((clause, filter) => {
446
- const escapedColumn = filter.column.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
447
- const pattern = new RegExp(`\\[${escapedColumn}\\]`, "g");
448
- return clause.replace(pattern, `[t].[${filter.column}]`);
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);
449
475
  }, whereClause);
450
- const countQuery = `SELECT COUNT(*) as total FROM [${schema}].[${table}]${whereClause ? " " + 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 : ""}`;
451
479
  const countResult = await executeQuery(countQuery, filterParams.length > 0 ? filterParams : []);
452
480
  const total = countResult[0]?.total || 0;
453
481
  let orderByColumn = sortColumn || "";
@@ -455,17 +483,19 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
455
483
  if (orderByColumn) {
456
484
  try {
457
485
  const validateQuery = `
458
- SELECT COLUMN_NAME
459
- FROM INFORMATION_SCHEMA.COLUMNS
460
- 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)}
461
489
  `;
462
490
  const validateResult = await executeQuery(validateQuery, [
463
- { name: "schema", value: schema, type: sql.NVarChar },
464
- { name: "table", value: table, type: sql.NVarChar },
465
- { name: "column", value: orderByColumn, type: sql.NVarChar }
491
+ { name: "schema", value: schema },
492
+ { name: "table", value: table },
493
+ { name: "column", value: orderByColumn }
466
494
  ]);
467
495
  if (validateResult.length === 0) {
468
496
  orderByColumn = "";
497
+ } else {
498
+ orderByColumn = validateResult[0].column_name || validateResult[0].COLUMN_NAME || orderByColumn;
469
499
  }
470
500
  } catch (e) {
471
501
  orderByColumn = "";
@@ -473,18 +503,20 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
473
503
  }
474
504
  if (!orderByColumn) {
475
505
  try {
506
+ const topClause = dialect.topN(1);
476
507
  const structureQuery = `
477
- SELECT TOP 1 COLUMN_NAME
478
- FROM INFORMATION_SCHEMA.COLUMNS
479
- WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table
480
- 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) : ""}
481
513
  `;
482
514
  const structureResult = await executeQuery(structureQuery, [
483
- { name: "schema", value: schema, type: sql.NVarChar },
484
- { name: "table", value: table, type: sql.NVarChar }
515
+ { name: "schema", value: schema },
516
+ { name: "table", value: table }
485
517
  ]);
486
518
  if (structureResult.length > 0) {
487
- orderByColumn = structureResult[0].COLUMN_NAME;
519
+ orderByColumn = structureResult[0].column_name || structureResult[0].COLUMN_NAME;
488
520
  }
489
521
  } catch (e) {
490
522
  }
@@ -494,48 +526,48 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
494
526
  const fkDisplayColumns = {};
495
527
  const fkQuery = `
496
528
  SELECT
497
- kcu1.COLUMN_NAME as fkColumnName,
498
- kcu2.TABLE_SCHEMA as referencedSchema,
499
- kcu2.TABLE_NAME as referencedTable,
500
- kcu2.COLUMN_NAME as referencedColumn
501
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
502
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
503
- ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
504
- AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
505
- AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
506
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
507
- ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
508
- AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
509
- AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
510
- AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
511
- WHERE kcu1.TABLE_SCHEMA = @schema
512
- 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)}
513
545
  `;
514
546
  console.log("Fetching foreign keys with query:", fkQuery);
515
547
  const foreignKeys = await executeQuery(fkQuery, [
516
- { name: "schema", value: schema, type: sql.NVarChar },
517
- { name: "table", value: table, type: sql.NVarChar }
548
+ { name: "schema", value: schema },
549
+ { name: "table", value: table }
518
550
  ]);
519
551
  console.log(`Found ${foreignKeys.length} foreign key(s)`);
520
552
  if (foreignKeys.length > 0) {
521
553
  const uniqueRefTables = Array.from(
522
- new Set(foreignKeys.map((fk) => `${fk.referencedSchema}.${fk.referencedTable}`))
554
+ new Set(foreignKeys.map((fk) => `${fk.referencedSchema || fk.referenced_schema}.${fk.referencedTable || fk.referenced_table}`))
523
555
  );
524
556
  const tableConditions = uniqueRefTables.map((tableRef, idx) => {
525
557
  const [refSchema, refTable] = tableRef.split(".");
526
- return `(TABLE_SCHEMA = @refSchema${idx} AND TABLE_NAME = @refTable${idx})`;
558
+ return `(table_schema = ${dialect.param(idx * 2 + 1)} AND table_name = ${dialect.param(idx * 2 + 2)})`;
527
559
  }).join(" OR ");
528
560
  const batchColumnsQuery = `
529
- SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION
530
- FROM INFORMATION_SCHEMA.COLUMNS
561
+ SELECT table_schema, table_name, column_name, data_type, ordinal_position
562
+ FROM information_schema.columns
531
563
  WHERE ${tableConditions}
532
- ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
564
+ ORDER BY table_schema, table_name, ordinal_position
533
565
  `;
534
- const batchParams = uniqueRefTables.flatMap((tableRef, idx) => {
566
+ const batchParams = uniqueRefTables.flatMap((tableRef) => {
535
567
  const [refSchema, refTable] = tableRef.split(".");
536
568
  return [
537
- { name: `refSchema${idx}`, value: refSchema, type: sql.NVarChar },
538
- { name: `refTable${idx}`, value: refTable, type: sql.NVarChar }
569
+ { name: "refSchema", value: refSchema },
570
+ { name: "refTable", value: refTable }
539
571
  ];
540
572
  });
541
573
  console.log("Fetching referenced table columns with query:", batchColumnsQuery);
@@ -544,37 +576,41 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
544
576
  console.log(`Found columns for ${uniqueRefTables.length} referenced table(s)`);
545
577
  const columnsByTable = {};
546
578
  allRefColumns.forEach((col) => {
547
- const key = `${col.TABLE_SCHEMA}.${col.TABLE_NAME}`;
579
+ const schemaName = col.table_schema || col.TABLE_SCHEMA;
580
+ const tableName = col.table_name || col.TABLE_NAME;
581
+ const key = `${schemaName}.${tableName}`;
548
582
  if (!columnsByTable[key]) {
549
583
  columnsByTable[key] = [];
550
584
  }
551
585
  columnsByTable[key].push(col);
552
586
  });
553
587
  for (const fk of foreignKeys) {
554
- const fkColumn = fk.fkColumnName;
555
- const refSchema = fk.referencedSchema;
556
- const refTable = fk.referencedTable;
557
- const refColumn = fk.referencedColumn;
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;
558
592
  const tableKey = `${refSchema}.${refTable}`;
559
593
  const refColumns = columnsByTable[tableKey] || [];
560
594
  const preferredNames = ["name", "title", "description", "code"];
561
595
  let displayColumn = null;
562
596
  for (const preferredName of preferredNames) {
563
- const found = refColumns.find(
564
- (col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
565
- );
597
+ const found = refColumns.find((col) => {
598
+ const colName = col.column_name || col.COLUMN_NAME;
599
+ return colName.toLowerCase() === preferredName.toLowerCase();
600
+ });
566
601
  if (found) {
567
- displayColumn = found.COLUMN_NAME;
602
+ displayColumn = found.column_name || found.COLUMN_NAME;
568
603
  break;
569
604
  }
570
605
  }
571
606
  if (!displayColumn) {
572
607
  const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
573
- const found = refColumns.find(
574
- (col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
575
- );
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
+ });
576
612
  if (found) {
577
- displayColumn = found.COLUMN_NAME;
613
+ displayColumn = found.column_name || found.COLUMN_NAME;
578
614
  }
579
615
  }
580
616
  if (displayColumn) {
@@ -588,89 +624,79 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
588
624
  refColumn,
589
625
  displayColumn
590
626
  });
591
- fkSelects.push(`${alias}.[${displayColumn}] as [${fkColumn}_display]`);
627
+ fkSelects.push(`${dialect.quoteId(alias)}.${dialect.quoteId(displayColumn)} as ${dialect.quoteId(`${fkColumn}_display`)}`);
592
628
  }
593
629
  fkDisplayColumns[fkColumn] = displayColumn;
594
630
  }
595
631
  }
596
632
  }
597
633
  const baseTableAlias = "t";
598
- let baseSelects = `[${baseTableAlias}].*`;
634
+ const quotedTableAlias = dialect.quoteId(baseTableAlias);
635
+ let baseSelects = `${quotedTableAlias}.*`;
599
636
  if (fkDisplayMode === "display-only") {
600
637
  const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
601
638
  if (fkColumnNames.length > 0) {
602
- baseSelects = `[${baseTableAlias}].*`;
639
+ baseSelects = `${quotedTableAlias}.*`;
603
640
  }
604
641
  }
605
642
  const allSelects = `${baseSelects}${fkSelects.length > 0 ? ", " + fkSelects.join(", ") : ""}`;
606
643
  const buildJoinClauses = (tableAlias) => {
607
- return fkJoins.map(
608
- (fk) => `LEFT JOIN [${fk.refSchema}].[${fk.refTable}] ${fk.alias} ON [${tableAlias}].[${fk.fkColumn}] = ${fk.alias}.[${fk.refColumn}]`
609
- ).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 ");
610
653
  };
611
654
  let data;
612
655
  let generatedQuery = "";
656
+ const offsetParamIndex = filterParams.length + 1;
657
+ const pageSizeParamIndex = filterParams.length + 2;
613
658
  if (orderByColumn) {
659
+ const quotedOrderByColumn = dialect.quoteId(orderByColumn);
660
+ const limitOffsetClause = dialect.limitOffset(offset, pageSize);
614
661
  const dataQuery = `
615
662
  SELECT ${allSelects}
616
- FROM [${schema}].[${table}] ${baseTableAlias}
663
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}
617
664
  ${buildJoinClauses(baseTableAlias)}
618
665
  ${qualifiedDataWhereClause}
619
- ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
620
- OFFSET @offset ROWS
621
- FETCH NEXT @pageSize ROWS ONLY
666
+ ORDER BY ${quotedTableAlias}.${quotedOrderByColumn} ${orderByDirection}
667
+ ${limitOffsetClause}
622
668
  `;
623
669
  generatedQuery = `SELECT ${allSelects}
624
- FROM [${schema}].[${table}] ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${qualifiedDataWhereClause ? "\n" + qualifiedDataWhereClause : ""}
625
- ORDER BY ${baseTableAlias}.[${orderByColumn}] ${orderByDirection}
626
- OFFSET ${offset} ROWS
627
- 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}`;
628
673
  console.log("Executing SQL query:", dataQuery);
629
674
  console.log("Query parameters:", {
630
675
  offset,
631
676
  pageSize,
632
677
  filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
633
678
  });
634
- data = await executeQuery(dataQuery, [
635
- { name: "offset", value: offset, type: sql.Int },
636
- { name: "pageSize", value: pageSize, type: sql.Int },
637
- ...filterParams
638
- ]);
679
+ data = await executeQuery(dataQuery, filterParams);
639
680
  } else {
640
- const innerQuery = `
641
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
642
- FROM [${schema}].[${table}]
643
- ${whereClause}
644
- `;
681
+ const dbType = getDbType();
682
+ const limitOffsetClause = dialect.limitOffset(offset, pageSize);
645
683
  const dataQuery = `
646
684
  SELECT ${allSelects}
647
- FROM (${innerQuery}) ${baseTableAlias}
685
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}
648
686
  ${buildJoinClauses(baseTableAlias)}
649
- WHERE ${baseTableAlias}.rn > @offset AND ${baseTableAlias}.rn <= @offset + @pageSize
650
- ORDER BY ${baseTableAlias}.rn
687
+ ${qualifiedDataWhereClause}
688
+ ${limitOffsetClause}
651
689
  `;
652
690
  generatedQuery = `SELECT ${allSelects}
653
- FROM (
654
- SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
655
- FROM [${schema}].[${table}]${whereClause ? "\n " + whereClause : ""}
656
- ) ${baseTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}
657
- WHERE ${baseTableAlias}.rn > ${offset} AND ${baseTableAlias}.rn <= ${offset + pageSize}
658
- ORDER BY ${baseTableAlias}.rn`;
691
+ FROM ${quotedSchema}.${quotedTable} ${quotedTableAlias}${fkJoins.length > 0 ? "\n" + buildJoinClauses(baseTableAlias) : ""}${qualifiedDataWhereClause ? "\n" + qualifiedDataWhereClause : ""}
692
+ ${limitOffsetClause}`;
659
693
  console.log("Executing SQL query:", dataQuery);
660
694
  console.log("Query parameters:", {
661
695
  offset,
662
696
  pageSize,
663
697
  filterParams: filterParams.map((p) => ({ name: p.name, value: p.value }))
664
698
  });
665
- data = await executeQuery(dataQuery, [
666
- { name: "offset", value: offset, type: sql.Int },
667
- { name: "pageSize", value: pageSize, type: sql.Int },
668
- ...filterParams
669
- ]);
670
- data = data.map((row) => {
671
- const { rn, ...rest } = row;
672
- return rest;
673
- });
699
+ data = await executeQuery(dataQuery, filterParams);
674
700
  }
675
701
  if (fkDisplayMode === "display-only") {
676
702
  const fkColumnNames = fkJoins.map((fk) => fk.fkColumn);
@@ -698,10 +724,22 @@ ORDER BY ${baseTableAlias}.rn`;
698
724
  const paramValues = new Map(
699
725
  filterParams.map((p) => [p.name, p.value])
700
726
  );
701
- generatedQuery = generatedQuery.replace(/@([A-Za-z0-9_]+)/g, (full, name) => {
702
- if (!paramValues.has(name)) return full;
703
- return formatSqlLiteral(paramValues.get(name));
704
- });
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
+ }
705
743
  }
706
744
  res.json({
707
745
  data,
@@ -718,8 +756,8 @@ ORDER BY ${baseTableAlias}.rn`;
718
756
  } catch (error) {
719
757
  console.error("Error fetching table data:", error);
720
758
  const errorMessage = error.message || "";
721
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
722
- 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");
723
761
  await disconnect2();
724
762
  }
725
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";
@@ -746,73 +784,68 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
746
784
  return res.status(400).json({ error: "Missing required parameters" });
747
785
  }
748
786
  const pool = getConnection();
749
- if (!pool || !pool.connected) {
787
+ if (!pool) {
750
788
  return res.status(400).json({ error: "Not connected to database" });
751
789
  }
790
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
791
+ if (!isConnected) {
792
+ return res.status(400).json({ error: "Not connected to database" });
793
+ }
794
+ const dialect = getDialect();
752
795
  const columnsQuery = `
753
- SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
754
- FROM INFORMATION_SCHEMA.COLUMNS
755
- WHERE TABLE_SCHEMA = @refSchema
756
- AND TABLE_NAME = @refTable
757
- 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
758
801
  `;
759
802
  const columns = await executeQuery(columnsQuery, [
760
- { name: "refSchema", value: referencedSchema, type: sql.NVarChar },
761
- { name: "refTable", value: referencedTable, type: sql.NVarChar }
803
+ { name: "refSchema", value: referencedSchema },
804
+ { name: "refTable", value: referencedTable }
762
805
  ]);
763
- const referencedColInfo = columns.find(
764
- (col) => col.COLUMN_NAME === referencedColumn
765
- );
806
+ const referencedColInfo = columns.find((col) => {
807
+ const colName = col.column_name || col.COLUMN_NAME;
808
+ return colName === referencedColumn;
809
+ });
766
810
  if (!referencedColInfo) {
767
811
  return res.status(400).json({ error: `Referenced column '${referencedColumn}' not found` });
768
812
  }
769
- const getSqlType = (dataType) => {
770
- const dt = dataType.toLowerCase();
771
- if (dt === "int" || dt === "integer") return sql.Int;
772
- if (dt === "bigint") return sql.BigInt;
773
- if (dt === "smallint") return sql.SmallInt;
774
- if (dt === "tinyint") return sql.TinyInt;
775
- if (dt === "bit") return sql.Bit;
776
- if (dt === "float" || dt === "real" || dt === "double precision") return sql.Float;
777
- if (dt === "decimal" || dt === "numeric" || dt === "money" || dt === "smallmoney") return sql.Decimal(18, 0);
778
- if (dt === "datetime" || dt === "datetime2" || dt === "smalldatetime") return sql.DateTime;
779
- if (dt === "date") return sql.Date;
780
- if (dt === "time") return sql.Time;
781
- if (dt === "uniqueidentifier") return sql.UniqueIdentifier;
782
- return sql.NVarChar;
783
- };
784
- const referencedColumnType = getSqlType(referencedColInfo.DATA_TYPE);
785
813
  const preferredNames = ["name", "title", "description", "code"];
786
814
  let displayColumn = null;
787
815
  for (const preferredName of preferredNames) {
788
- const found = columns.find(
789
- (col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase()
790
- );
816
+ const found = columns.find((col) => {
817
+ const colName = col.column_name || col.COLUMN_NAME;
818
+ return colName.toLowerCase() === preferredName.toLowerCase();
819
+ });
791
820
  if (found) {
792
- displayColumn = found.COLUMN_NAME;
821
+ displayColumn = found.column_name || found.COLUMN_NAME;
793
822
  break;
794
823
  }
795
824
  }
796
825
  if (!displayColumn) {
797
826
  const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
798
- const found = columns.find(
799
- (col) => stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
800
- );
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
+ });
801
831
  if (found) {
802
- displayColumn = found.COLUMN_NAME;
832
+ displayColumn = found.column_name || found.COLUMN_NAME;
803
833
  }
804
834
  }
805
- const placeholders = ids.map((_, i) => `@id${i}`).join(", ");
806
- 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;
807
841
  const dataQuery = `
808
842
  SELECT ${selectColumns}
809
- FROM [${referencedSchema}].[${referencedTable}]
810
- WHERE [${referencedColumn}] IN (${placeholders})
843
+ FROM ${quotedRefSchema}.${quotedRefTable}
844
+ WHERE ${quotedRefColumn} IN (${placeholders})
811
845
  `;
812
- const params = ids.map((id, i) => ({
813
- name: `id${i}`,
814
- value: id,
815
- type: referencedColumnType
846
+ const params = ids.map((id) => ({
847
+ name: "id",
848
+ value: id
816
849
  }));
817
850
  const result = await executeQuery(dataQuery, params);
818
851
  const dataMap = {};
@@ -824,8 +857,8 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
824
857
  } catch (error) {
825
858
  console.error("Error fetching related data:", error);
826
859
  const errorMessage = error.message || "";
827
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
828
- 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");
829
862
  await disconnect2();
830
863
  }
831
864
  res.status(500).json({ error: error.message || "Failed to fetch related data" });
@@ -837,148 +870,163 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
837
870
  const searchQuery = req.query.search;
838
871
  const columnsParam = req.query.columns;
839
872
  const pool = getConnection();
840
- if (!pool || !pool.connected) {
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) {
841
878
  return res.status(400).json({ error: "Not connected to database" });
842
879
  }
880
+ const dialect = getDialect();
843
881
  const columnQuery = `
844
882
  SELECT
845
- c.COLUMN_NAME,
846
- c.DATA_TYPE,
847
- fk.REFERENCED_TABLE_SCHEMA as referencedSchema,
848
- fk.REFERENCED_TABLE_NAME as referencedTable,
849
- fk.REFERENCED_COLUMN_NAME as referencedColumn
850
- FROM INFORMATION_SCHEMA.COLUMNS c
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
851
889
  LEFT JOIN (
852
890
  SELECT
853
- kcu1.TABLE_SCHEMA,
854
- kcu1.TABLE_NAME,
855
- kcu1.COLUMN_NAME,
856
- kcu2.TABLE_SCHEMA as REFERENCED_TABLE_SCHEMA,
857
- kcu2.TABLE_NAME as REFERENCED_TABLE_NAME,
858
- kcu2.COLUMN_NAME as REFERENCED_COLUMN_NAME
859
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
860
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu1
861
- ON rc.CONSTRAINT_CATALOG = kcu1.CONSTRAINT_CATALOG
862
- AND rc.CONSTRAINT_SCHEMA = kcu1.CONSTRAINT_SCHEMA
863
- AND rc.CONSTRAINT_NAME = kcu1.CONSTRAINT_NAME
864
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu2
865
- ON rc.UNIQUE_CONSTRAINT_CATALOG = kcu2.CONSTRAINT_CATALOG
866
- AND rc.UNIQUE_CONSTRAINT_SCHEMA = kcu2.CONSTRAINT_SCHEMA
867
- AND rc.UNIQUE_CONSTRAINT_NAME = kcu2.CONSTRAINT_NAME
868
- AND kcu1.ORDINAL_POSITION = kcu2.ORDINAL_POSITION
869
- ) fk ON c.TABLE_SCHEMA = fk.TABLE_SCHEMA
870
- AND c.TABLE_NAME = fk.TABLE_NAME
871
- AND c.COLUMN_NAME = fk.COLUMN_NAME
872
- WHERE c.TABLE_SCHEMA = @schema AND c.TABLE_NAME = @table AND c.COLUMN_NAME = @column
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)}
873
911
  `;
874
912
  const columnResult = await executeQuery(columnQuery, [
875
- { name: "schema", value: schema, type: sql.NVarChar },
876
- { name: "table", value: table, type: sql.NVarChar },
877
- { name: "column", value: column, type: sql.NVarChar }
913
+ { name: "schema", value: schema },
914
+ { name: "table", value: table },
915
+ { name: "column", value: column }
878
916
  ]);
879
917
  if (columnResult.length === 0) {
880
918
  return res.status(400).json({ error: "Column not found" });
881
919
  }
882
920
  const columnInfo = columnResult[0];
883
- const isForeignKey = !!columnInfo.referencedSchema && !!columnInfo.referencedTable;
921
+ const isForeignKey = !!(columnInfo.referencedSchema || columnInfo.referenced_schema) && !!(columnInfo.referencedTable || columnInfo.referenced_table);
884
922
  let query = "";
885
923
  const params = [];
886
924
  let displayColumn = null;
887
925
  if (isForeignKey) {
888
- const refSchema = columnInfo.referencedSchema;
889
- const refTable = columnInfo.referencedTable;
890
- const refColumn = columnInfo.referencedColumn;
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);
891
932
  let columnsToSelect = [];
892
933
  if (columnsParam) {
893
934
  columnsToSelect = columnsParam.split(",").map((c) => c.trim()).filter((c) => c);
894
935
  }
895
936
  if (columnsToSelect.length > 0) {
896
- const selectCols = columnsToSelect.map((col) => `[${col}]`).join(", ");
897
- query = `SELECT DISTINCT ${selectCols} FROM [${refSchema}].[${refTable}]`;
937
+ const selectCols = columnsToSelect.map((col) => dialect.quoteId(col)).join(", ");
938
+ query = `SELECT DISTINCT ${selectCols} FROM ${quotedRefSchema}.${quotedRefTable}`;
898
939
  if (searchQuery && searchQuery.trim()) {
899
- const searchCols = columnsToSelect.map((col) => `[${col}] LIKE @search`).join(" OR ");
900
- query += ` WHERE (${searchCols}) AND [${refColumn}] IS NOT NULL`;
901
- params.push({ name: "search", value: `%${searchQuery.trim()}%`, type: sql.NVarChar });
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()}%` });
902
943
  } else {
903
- query += ` WHERE [${refColumn}] IS NOT NULL`;
944
+ query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
904
945
  }
905
946
  const orderByCol = columnsToSelect.length > 1 ? columnsToSelect[1] : columnsToSelect[0];
906
- query += ` ORDER BY [${orderByCol}] OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY`;
947
+ query += ` ORDER BY ${dialect.quoteId(orderByCol)} ${dialect.limitOffset(0, 1e3)}`;
907
948
  } else {
908
949
  const refColumnsQuery = `
909
- SELECT COLUMN_NAME, DATA_TYPE
910
- FROM INFORMATION_SCHEMA.COLUMNS
911
- WHERE TABLE_SCHEMA = @refSchema AND TABLE_NAME = @refTable
912
- ORDER BY ORDINAL_POSITION
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
913
954
  `;
914
955
  const refColumns = await executeQuery(refColumnsQuery, [
915
- { name: "refSchema", value: refSchema, type: sql.NVarChar },
916
- { name: "refTable", value: refTable, type: sql.NVarChar }
956
+ { name: "refSchema", value: refSchema },
957
+ { name: "refTable", value: refTable }
917
958
  ]);
918
959
  const preferredNames = ["name", "title", "description", "code"];
919
960
  for (const preferredName of preferredNames) {
920
- const found = refColumns.find(
921
- (col) => col.COLUMN_NAME.toLowerCase() === preferredName.toLowerCase() && col.COLUMN_NAME.toLowerCase() !== refColumn.toLowerCase()
922
- );
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
+ });
923
965
  if (found) {
924
- displayColumn = found.COLUMN_NAME;
966
+ displayColumn = found.column_name || found.COLUMN_NAME;
925
967
  break;
926
968
  }
927
969
  }
928
970
  if (!displayColumn) {
929
971
  const stringTypes = ["varchar", "nvarchar", "char", "nchar", "text", "ntext"];
930
- const found = refColumns.find(
931
- (col) => col.COLUMN_NAME.toLowerCase() !== refColumn.toLowerCase() && stringTypes.some((type) => col.DATA_TYPE.toLowerCase().includes(type))
932
- );
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
+ });
933
977
  if (found) {
934
- displayColumn = found.COLUMN_NAME;
978
+ displayColumn = found.column_name || found.COLUMN_NAME;
935
979
  }
936
980
  }
937
- const selectCols = displayColumn ? `[${refColumn}], [${displayColumn}]` : `[${refColumn}]`;
938
- query = `SELECT DISTINCT ${selectCols} FROM [${refSchema}].[${refTable}]`;
981
+ const quotedDisplayColumn = displayColumn ? dialect.quoteId(displayColumn) : null;
982
+ const selectCols = displayColumn ? `${quotedRefColumn}, ${quotedDisplayColumn}` : quotedRefColumn;
983
+ query = `SELECT DISTINCT ${selectCols} FROM ${quotedRefSchema}.${quotedRefTable}`;
939
984
  if (searchQuery && searchQuery.trim()) {
940
985
  if (displayColumn) {
941
- query += ` WHERE [${displayColumn}] LIKE @search OR [${refColumn}] LIKE @search`;
986
+ query += ` WHERE ${quotedDisplayColumn} LIKE ${dialect.param(1)} OR ${quotedRefColumn} LIKE ${dialect.param(1)}`;
942
987
  } else {
943
- query += ` WHERE [${refColumn}] LIKE @search`;
988
+ query += ` WHERE ${quotedRefColumn} LIKE ${dialect.param(1)}`;
944
989
  }
945
- params.push({ name: "search", value: `%${searchQuery.trim()}%`, type: sql.NVarChar });
946
- query += ` AND [${refColumn}] IS NOT NULL`;
990
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
991
+ query += ` AND ${quotedRefColumn} IS NOT NULL`;
947
992
  } else {
948
- query += ` WHERE [${refColumn}] IS NOT NULL`;
993
+ query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
949
994
  }
950
- query += ` ORDER BY ${displayColumn ? `[${displayColumn}]` : `[${refColumn}]`} OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY`;
995
+ query += ` ORDER BY ${displayColumn ? quotedDisplayColumn : quotedRefColumn} ${dialect.limitOffset(0, 1e3)}`;
951
996
  }
952
997
  } else {
953
- const dataType = columnInfo.DATA_TYPE.toLowerCase();
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);
954
1002
  const columnsToSelect = columnsParam ? columnsParam.split(",").map((c) => c.trim()).filter((c) => c) : [];
955
1003
  if (columnsToSelect.length > 0) {
956
- const escapedColumns = columnsToSelect.map((col) => col.replace(/]/g, "]]"));
957
- const keyColumn = escapedColumns[0];
958
- const orderByColumn = escapedColumns.length > 1 ? escapedColumns[1] : keyColumn;
959
- query = `SELECT DISTINCT ${escapedColumns.map((col) => `[${col}]`).join(", ")} FROM [${schema}].[${table}]`;
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}`;
960
1008
  if (searchQuery && searchQuery.trim()) {
961
- const searchCols = escapedColumns.map((col) => `TRY_CAST([${col}] AS NVARCHAR(4000)) LIKE @search`).join(" OR ");
962
- query += ` WHERE (${searchCols}) AND [${keyColumn}] IS NOT NULL`;
963
- params.push({ name: "search", value: `%${searchQuery.trim()}%`, type: sql.NVarChar });
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()}%` });
964
1012
  } else {
965
- query += ` WHERE [${keyColumn}] IS NOT NULL`;
1013
+ query += ` WHERE ${keyColumn} IS NOT NULL`;
966
1014
  }
967
- query += ` ORDER BY [${orderByColumn}] OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY`;
1015
+ query += ` ORDER BY ${orderByColumn} ${dialect.limitOffset(0, 1e3)}`;
968
1016
  } else {
969
- query = `SELECT DISTINCT [${column}] FROM [${schema}].[${table}]`;
1017
+ query = `SELECT DISTINCT ${quotedColumn} FROM ${quotedSchema}.${quotedTable}`;
970
1018
  if (searchQuery && searchQuery.trim()) {
971
1019
  if (["varchar", "nvarchar", "char", "nchar", "text", "ntext"].some((t) => dataType.includes(t))) {
972
- query += ` WHERE [${column}] LIKE @search`;
973
- params.push({ name: "search", value: `%${searchQuery.trim()}%`, type: sql.NVarChar });
974
- query += ` AND [${column}] IS NOT NULL`;
1020
+ query += ` WHERE ${quotedColumn} LIKE ${dialect.param(1)}`;
1021
+ params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1022
+ query += ` AND ${quotedColumn} IS NOT NULL`;
975
1023
  } else {
976
- query += ` WHERE [${column}] IS NOT NULL`;
1024
+ query += ` WHERE ${quotedColumn} IS NOT NULL`;
977
1025
  }
978
1026
  } else {
979
- query += ` WHERE [${column}] IS NOT NULL`;
1027
+ query += ` WHERE ${quotedColumn} IS NOT NULL`;
980
1028
  }
981
- query += ` ORDER BY [${column}] OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY`;
1029
+ query += ` ORDER BY ${quotedColumn} ${dialect.limitOffset(0, 1e3)}`;
982
1030
  }
983
1031
  }
984
1032
  const result = await executeQuery(query, params);
@@ -986,8 +1034,8 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
986
1034
  } catch (error) {
987
1035
  console.error("Error fetching distinct values:", error);
988
1036
  const errorMessage = error.message || "";
989
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
990
- const { disconnect: disconnect2 } = await import("./mssql-AYS72PRQ.js");
1037
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
1038
+ const { disconnect: disconnect2 } = await import("./db-OIJ2PJK6.js");
991
1039
  await disconnect2();
992
1040
  }
993
1041
  res.status(500).json({ error: error.message || "Failed to fetch distinct values" });
@@ -999,24 +1047,33 @@ import { Router as Router3 } from "express";
999
1047
  var queryRoutes = Router3();
1000
1048
  queryRoutes.post("/", async (req, res) => {
1001
1049
  try {
1002
- const { query: sqlQuery } = req.body;
1050
+ const { query: sqlQuery, queryId } = req.body;
1003
1051
  if (!sqlQuery || typeof sqlQuery !== "string") {
1004
1052
  return res.status(400).json({ error: "Query is required" });
1005
1053
  }
1054
+ const activeQueryId = queryId || generateQueryId();
1006
1055
  const startTime = Date.now();
1007
- const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery);
1056
+ const { recordsets: resultSets, columnMetadata } = await executeQueryMultiple(sqlQuery, void 0, activeQueryId);
1008
1057
  const executionTime = Date.now() - startTime;
1009
1058
  res.json({
1010
1059
  data: resultSets[0] || [],
1011
1060
  resultSets: resultSets.length > 0 ? resultSets : [],
1012
1061
  executionTime,
1013
- columnMetadata
1062
+ columnMetadata,
1014
1063
  // Include column metadata for empty result sets
1064
+ queryId: activeQueryId
1065
+ // Return queryId so client can track it
1015
1066
  });
1016
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
+ }
1017
1074
  const errorMessage = error.message || "";
1018
- if (errorMessage.includes("Login failed") || errorMessage.includes("authentication")) {
1019
- 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");
1020
1077
  await disconnect2();
1021
1078
  }
1022
1079
  res.status(500).json({
@@ -1025,6 +1082,24 @@ queryRoutes.post("/", async (req, res) => {
1025
1082
  });
1026
1083
  }
1027
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
+ });
1028
1103
 
1029
1104
  // src/server/index.ts
1030
1105
  var __filename = fileURLToPath(import.meta.url);
@@ -1085,7 +1160,7 @@ function getProvidedConnectionString() {
1085
1160
  // src/cli/index.ts
1086
1161
  import open from "open";
1087
1162
  var program = new Command();
1088
- 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) => {
1089
1164
  const port = parseInt(options?.port || "4983", 10);
1090
1165
  try {
1091
1166
  const server = await startServer(port, connectionString);
@@ -1093,7 +1168,8 @@ program.name("datapeek").description("A local SQL database browser").version("0.
1093
1168
  console.log(`
1094
1169
  \u{1F680} Datapeek server running at ${url}`);
1095
1170
  if (connectionString) {
1096
- 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)}...`);
1097
1173
  }
1098
1174
  console.log(`
1099
1175
  Press Ctrl+C to stop