@xcr1234/dbhub-fork 1.0.0

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 (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/dist/chunk-6O5I6UAW.js +2150 -0
  4. package/dist/chunk-BRXZ5ZQB.js +127 -0
  5. package/dist/chunk-C7WEAPX4.js +485 -0
  6. package/dist/chunk-FAIJPBT5.js +40 -0
  7. package/dist/chunk-JFWX35TB.js +34 -0
  8. package/dist/chunk-RTB262PR.js +60 -0
  9. package/dist/chunk-WVVMH6FJ.js +49 -0
  10. package/dist/demo/employee-sqlite/employee.sql +117 -0
  11. package/dist/demo/employee-sqlite/load_department.sql +10 -0
  12. package/dist/demo/employee-sqlite/load_dept_emp.sql +1103 -0
  13. package/dist/demo/employee-sqlite/load_dept_manager.sql +17 -0
  14. package/dist/demo/employee-sqlite/load_employee.sql +1000 -0
  15. package/dist/demo/employee-sqlite/load_salary1.sql +9488 -0
  16. package/dist/demo/employee-sqlite/load_title.sql +1470 -0
  17. package/dist/demo-loader-WKQAEFSX.js +48 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1584 -0
  20. package/dist/mariadb-QHRMVK47.js +468 -0
  21. package/dist/mysql-GOLPG2Q6.js +475 -0
  22. package/dist/oracle-3V2T7ZWF.js +27739 -0
  23. package/dist/postgres-CK6N5BXI.js +520 -0
  24. package/dist/public/assets/index-BiTHVJQj.js +144 -0
  25. package/dist/public/assets/index-CfPYb3wl.css +1 -0
  26. package/dist/public/assets/postgres-BpcazhJg.svg +22 -0
  27. package/dist/public/assets/sqlserver-ByfFYYpV.svg +11 -0
  28. package/dist/public/favicon.svg +57 -0
  29. package/dist/public/index.html +14 -0
  30. package/dist/public/logo-full-light.svg +58 -0
  31. package/dist/registry-KI3KJMRY.js +13 -0
  32. package/dist/sqlite-P6NXTMYE.js +2473 -0
  33. package/dist/sqlserver-5RM44GWI.js +493 -0
  34. package/package.json +99 -0
@@ -0,0 +1,520 @@
1
+ import {
2
+ quoteIdentifier
3
+ } from "./chunk-JFWX35TB.js";
4
+ import {
5
+ SQLRowLimiter
6
+ } from "./chunk-BRXZ5ZQB.js";
7
+ import {
8
+ ConnectorRegistry,
9
+ SafeURL,
10
+ obfuscateDSNPassword,
11
+ splitSQLStatements
12
+ } from "./chunk-C7WEAPX4.js";
13
+ import "./chunk-FAIJPBT5.js";
14
+
15
+ // src/connectors/postgres/index.ts
16
+ import fs from "fs";
17
+ import os from "os";
18
+ import path from "path";
19
+ import pg from "pg";
20
+
21
+ // src/connectors/postgres/failed-to-read-certificate.ts
22
+ var FailedToReadCertificate = class extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "FailedToReadCertificate";
26
+ }
27
+ };
28
+
29
+ // src/connectors/postgres/index.ts
30
+ var { Pool } = pg;
31
+ var PostgresDSNParser = class {
32
+ async parse(dsn, config) {
33
+ const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
34
+ const queryTimeoutSeconds = config?.queryTimeoutSeconds;
35
+ if (!this.isValidDSN(dsn)) {
36
+ const obfuscatedDSN = obfuscateDSNPassword(dsn);
37
+ const expectedFormat = this.getSampleDSN();
38
+ throw new Error(
39
+ `Invalid PostgreSQL DSN format.
40
+ Provided: ${obfuscatedDSN}
41
+ Expected: ${expectedFormat}`
42
+ );
43
+ }
44
+ try {
45
+ const url = new SafeURL(dsn);
46
+ const poolConfig = {
47
+ host: url.hostname,
48
+ port: url.port ? parseInt(url.port) : 5432,
49
+ database: url.pathname ? url.pathname.substring(1) : "",
50
+ // Remove leading '/' if exists
51
+ user: url.username,
52
+ password: url.password
53
+ };
54
+ let sslmode;
55
+ let sslrootcert;
56
+ url.forEachSearchParam((value, key) => {
57
+ if (key === "sslmode") {
58
+ sslmode = value;
59
+ } else if (key === "sslrootcert") {
60
+ sslrootcert = value;
61
+ }
62
+ });
63
+ if (sslmode === "disable") {
64
+ poolConfig.ssl = false;
65
+ } else if (sslmode === "require") {
66
+ poolConfig.ssl = { rejectUnauthorized: false };
67
+ } else if (sslmode === "verify-ca" || sslmode === "verify-full") {
68
+ const sslConfig = { rejectUnauthorized: true };
69
+ if (sslmode === "verify-ca") {
70
+ sslConfig.checkServerIdentity = () => void 0;
71
+ }
72
+ if (sslrootcert) {
73
+ const certPath = sslrootcert.startsWith("~/") ? path.join(os.homedir(), sslrootcert.slice(2)) : sslrootcert;
74
+ try {
75
+ sslConfig.ca = await fs.promises.readFile(certPath, "utf-8");
76
+ } catch (err) {
77
+ throw new FailedToReadCertificate(
78
+ `Failed to read SSL root certificate at '${certPath}': ${err instanceof Error ? err.message : String(err)}`
79
+ );
80
+ }
81
+ }
82
+ poolConfig.ssl = sslConfig;
83
+ } else if (sslmode !== void 0) {
84
+ poolConfig.ssl = true;
85
+ }
86
+ if (connectionTimeoutSeconds !== void 0) {
87
+ poolConfig.connectionTimeoutMillis = connectionTimeoutSeconds * 1e3;
88
+ }
89
+ if (queryTimeoutSeconds !== void 0) {
90
+ poolConfig.query_timeout = queryTimeoutSeconds * 1e3;
91
+ }
92
+ return poolConfig;
93
+ } catch (error) {
94
+ if (error instanceof FailedToReadCertificate) {
95
+ throw error;
96
+ }
97
+ const originalError = error instanceof Error ? error : new Error(String(error));
98
+ throw new Error(
99
+ `Failed to parse PostgreSQL DSN: ${originalError.message}`,
100
+ { cause: originalError }
101
+ );
102
+ }
103
+ }
104
+ getSampleDSN() {
105
+ return "postgres://postgres:password@localhost:5432/postgres?sslmode=require";
106
+ }
107
+ isValidDSN(dsn) {
108
+ try {
109
+ return dsn.startsWith("postgres://") || dsn.startsWith("postgresql://");
110
+ } catch (error) {
111
+ return false;
112
+ }
113
+ }
114
+ };
115
+ var PostgresConnector = class _PostgresConnector {
116
+ constructor() {
117
+ this.id = "postgres";
118
+ this.name = "PostgreSQL";
119
+ this.dsnParser = new PostgresDSNParser();
120
+ this.pool = null;
121
+ // Source ID is set by ConnectorManager after cloning
122
+ this.sourceId = "default";
123
+ // Default schema for discovery methods (first entry from search_path, or "public")
124
+ this.defaultSchema = "public";
125
+ }
126
+ getId() {
127
+ return this.sourceId;
128
+ }
129
+ clone() {
130
+ return new _PostgresConnector();
131
+ }
132
+ async connect(dsn, initScript, config) {
133
+ this.defaultSchema = "public";
134
+ try {
135
+ const poolConfig = await this.dsnParser.parse(dsn, config);
136
+ if (config?.readonly) {
137
+ poolConfig.options = (poolConfig.options || "") + " -c default_transaction_read_only=on";
138
+ }
139
+ if (config?.searchPath) {
140
+ const schemas = config.searchPath.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
141
+ if (schemas.length > 0) {
142
+ this.defaultSchema = schemas[0];
143
+ const quotedSchemas = schemas.map((s) => quoteIdentifier(s, "postgres"));
144
+ const optionsValue = quotedSchemas.join(",").replace(/\\/g, "\\\\").replace(/ /g, "\\ ");
145
+ poolConfig.options = (poolConfig.options || "") + ` -c search_path=${optionsValue}`;
146
+ }
147
+ }
148
+ this.pool = new Pool(poolConfig);
149
+ const client = await this.pool.connect();
150
+ client.release();
151
+ } catch (err) {
152
+ console.error("Failed to connect to PostgreSQL database:", err);
153
+ throw err;
154
+ }
155
+ }
156
+ async disconnect() {
157
+ if (this.pool) {
158
+ await this.pool.end();
159
+ this.pool = null;
160
+ }
161
+ }
162
+ async getSchemas() {
163
+ if (!this.pool) {
164
+ throw new Error("Not connected to database");
165
+ }
166
+ const client = await this.pool.connect();
167
+ try {
168
+ const result = await client.query(`
169
+ SELECT schema_name
170
+ FROM information_schema.schemata
171
+ WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
172
+ ORDER BY schema_name
173
+ `);
174
+ return result.rows.map((row) => row.schema_name);
175
+ } finally {
176
+ client.release();
177
+ }
178
+ }
179
+ async getTables(schema) {
180
+ if (!this.pool) {
181
+ throw new Error("Not connected to database");
182
+ }
183
+ const client = await this.pool.connect();
184
+ try {
185
+ const schemaToUse = schema || this.defaultSchema;
186
+ const result = await client.query(
187
+ `
188
+ SELECT table_name
189
+ FROM information_schema.tables
190
+ WHERE table_schema = $1
191
+ ORDER BY table_name
192
+ `,
193
+ [schemaToUse]
194
+ );
195
+ return result.rows.map((row) => row.table_name);
196
+ } finally {
197
+ client.release();
198
+ }
199
+ }
200
+ async tableExists(tableName, schema) {
201
+ if (!this.pool) {
202
+ throw new Error("Not connected to database");
203
+ }
204
+ const client = await this.pool.connect();
205
+ try {
206
+ const schemaToUse = schema || this.defaultSchema;
207
+ const result = await client.query(
208
+ `
209
+ SELECT EXISTS (
210
+ SELECT FROM information_schema.tables
211
+ WHERE table_schema = $1
212
+ AND table_name = $2
213
+ )
214
+ `,
215
+ [schemaToUse, tableName]
216
+ );
217
+ return result.rows[0].exists;
218
+ } finally {
219
+ client.release();
220
+ }
221
+ }
222
+ async getTableIndexes(tableName, schema) {
223
+ if (!this.pool) {
224
+ throw new Error("Not connected to database");
225
+ }
226
+ const client = await this.pool.connect();
227
+ try {
228
+ const schemaToUse = schema || this.defaultSchema;
229
+ const result = await client.query(
230
+ `
231
+ SELECT
232
+ i.relname as index_name,
233
+ array_agg(a.attname) as column_names,
234
+ ix.indisunique as is_unique,
235
+ ix.indisprimary as is_primary
236
+ FROM
237
+ pg_class t,
238
+ pg_class i,
239
+ pg_index ix,
240
+ pg_attribute a,
241
+ pg_namespace ns
242
+ WHERE
243
+ t.oid = ix.indrelid
244
+ AND i.oid = ix.indexrelid
245
+ AND a.attrelid = t.oid
246
+ AND a.attnum = ANY(ix.indkey)
247
+ AND t.relkind = 'r'
248
+ AND t.relname = $1
249
+ AND ns.oid = t.relnamespace
250
+ AND ns.nspname = $2
251
+ GROUP BY
252
+ i.relname,
253
+ ix.indisunique,
254
+ ix.indisprimary
255
+ ORDER BY
256
+ i.relname
257
+ `,
258
+ [tableName, schemaToUse]
259
+ );
260
+ return result.rows.map((row) => ({
261
+ index_name: row.index_name,
262
+ column_names: row.column_names,
263
+ is_unique: row.is_unique,
264
+ is_primary: row.is_primary
265
+ }));
266
+ } finally {
267
+ client.release();
268
+ }
269
+ }
270
+ async getTableSchema(tableName, schema) {
271
+ if (!this.pool) {
272
+ throw new Error("Not connected to database");
273
+ }
274
+ const client = await this.pool.connect();
275
+ try {
276
+ const schemaToUse = schema || this.defaultSchema;
277
+ const result = await client.query(
278
+ `
279
+ SELECT
280
+ c.column_name,
281
+ c.data_type,
282
+ c.is_nullable,
283
+ c.column_default,
284
+ pgd.description
285
+ FROM information_schema.columns c
286
+ LEFT JOIN pg_catalog.pg_namespace nsp
287
+ ON nsp.nspname = c.table_schema
288
+ LEFT JOIN pg_catalog.pg_class cls
289
+ ON cls.relnamespace = nsp.oid
290
+ AND cls.relname = c.table_name
291
+ LEFT JOIN pg_catalog.pg_description pgd
292
+ ON pgd.objoid = cls.oid
293
+ AND pgd.objsubid = c.ordinal_position
294
+ WHERE c.table_schema = $1
295
+ AND c.table_name = $2
296
+ ORDER BY c.ordinal_position
297
+ `,
298
+ [schemaToUse, tableName]
299
+ );
300
+ return result.rows;
301
+ } finally {
302
+ client.release();
303
+ }
304
+ }
305
+ async getTableRowCount(tableName, schema) {
306
+ if (!this.pool) {
307
+ throw new Error("Not connected to database");
308
+ }
309
+ const client = await this.pool.connect();
310
+ try {
311
+ const schemaToUse = schema || this.defaultSchema;
312
+ const result = await client.query(
313
+ `
314
+ SELECT c.reltuples::bigint as count
315
+ FROM pg_class c
316
+ JOIN pg_namespace n ON n.oid = c.relnamespace
317
+ WHERE c.relname = $1
318
+ AND n.nspname = $2
319
+ AND c.relkind IN ('r','p','m','f')
320
+ `,
321
+ [tableName, schemaToUse]
322
+ );
323
+ if (result.rows.length > 0) {
324
+ const count = Number(result.rows[0].count);
325
+ return count >= 0 ? count : null;
326
+ }
327
+ return null;
328
+ } finally {
329
+ client.release();
330
+ }
331
+ }
332
+ async getTableComment(tableName, schema) {
333
+ if (!this.pool) {
334
+ throw new Error("Not connected to database");
335
+ }
336
+ const client = await this.pool.connect();
337
+ try {
338
+ const schemaToUse = schema || this.defaultSchema;
339
+ const result = await client.query(
340
+ `
341
+ SELECT obj_description(c.oid) as table_comment
342
+ FROM pg_catalog.pg_class c
343
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
344
+ WHERE c.relname = $1
345
+ AND n.nspname = $2
346
+ AND c.relkind IN ('r','p','m','f','v')
347
+ `,
348
+ [tableName, schemaToUse]
349
+ );
350
+ if (result.rows.length > 0) {
351
+ return result.rows[0].table_comment || null;
352
+ }
353
+ return null;
354
+ } finally {
355
+ client.release();
356
+ }
357
+ }
358
+ async getStoredProcedures(schema, routineType) {
359
+ if (!this.pool) {
360
+ throw new Error("Not connected to database");
361
+ }
362
+ const client = await this.pool.connect();
363
+ try {
364
+ const schemaToUse = schema || this.defaultSchema;
365
+ const params = [schemaToUse];
366
+ let typeFilter = "";
367
+ if (routineType === "function") {
368
+ typeFilter = " AND routine_type = 'FUNCTION'";
369
+ } else if (routineType === "procedure") {
370
+ typeFilter = " AND routine_type = 'PROCEDURE'";
371
+ }
372
+ const result = await client.query(
373
+ `
374
+ SELECT
375
+ routine_name
376
+ FROM information_schema.routines
377
+ WHERE routine_schema = $1${typeFilter}
378
+ ORDER BY routine_name
379
+ `,
380
+ params
381
+ );
382
+ return result.rows.map((row) => row.routine_name);
383
+ } finally {
384
+ client.release();
385
+ }
386
+ }
387
+ async getStoredProcedureDetail(procedureName, schema) {
388
+ if (!this.pool) {
389
+ throw new Error("Not connected to database");
390
+ }
391
+ const client = await this.pool.connect();
392
+ try {
393
+ const schemaToUse = schema || this.defaultSchema;
394
+ const result = await client.query(
395
+ `
396
+ SELECT
397
+ routine_name as procedure_name,
398
+ routine_type,
399
+ CASE WHEN routine_type = 'PROCEDURE' THEN 'procedure' ELSE 'function' END as procedure_type,
400
+ external_language as language,
401
+ data_type as return_type,
402
+ routine_definition as definition,
403
+ (
404
+ SELECT string_agg(
405
+ parameter_name || ' ' ||
406
+ parameter_mode || ' ' ||
407
+ data_type,
408
+ ', '
409
+ )
410
+ FROM information_schema.parameters
411
+ WHERE specific_schema = $1
412
+ AND specific_name = $2
413
+ AND parameter_name IS NOT NULL
414
+ ) as parameter_list
415
+ FROM information_schema.routines
416
+ WHERE routine_schema = $1
417
+ AND routine_name = $2
418
+ `,
419
+ [schemaToUse, procedureName]
420
+ );
421
+ if (result.rows.length === 0) {
422
+ throw new Error(`Stored procedure '${procedureName}' not found in schema '${schemaToUse}'`);
423
+ }
424
+ const procedure = result.rows[0];
425
+ let definition = procedure.definition;
426
+ try {
427
+ const oidResult = await client.query(
428
+ `
429
+ SELECT p.oid, p.prosrc
430
+ FROM pg_proc p
431
+ JOIN pg_namespace n ON p.pronamespace = n.oid
432
+ WHERE p.proname = $1
433
+ AND n.nspname = $2
434
+ `,
435
+ [procedureName, schemaToUse]
436
+ );
437
+ if (oidResult.rows.length > 0) {
438
+ if (!definition) {
439
+ const oid = oidResult.rows[0].oid;
440
+ const defResult = await client.query(`SELECT pg_get_functiondef($1)`, [oid]);
441
+ if (defResult.rows.length > 0) {
442
+ definition = defResult.rows[0].pg_get_functiondef;
443
+ } else {
444
+ definition = oidResult.rows[0].prosrc;
445
+ }
446
+ }
447
+ }
448
+ } catch (err) {
449
+ console.error(`Error getting procedure definition: ${err}`);
450
+ }
451
+ return {
452
+ procedure_name: procedure.procedure_name,
453
+ procedure_type: procedure.procedure_type,
454
+ language: procedure.language || "sql",
455
+ parameter_list: procedure.parameter_list || "",
456
+ return_type: procedure.return_type !== "void" ? procedure.return_type : void 0,
457
+ definition: definition || void 0
458
+ };
459
+ } finally {
460
+ client.release();
461
+ }
462
+ }
463
+ async executeSQL(sql, options, parameters) {
464
+ if (!this.pool) {
465
+ throw new Error("Not connected to database");
466
+ }
467
+ const client = await this.pool.connect();
468
+ try {
469
+ const statements = splitSQLStatements(sql, "postgres");
470
+ if (statements.length === 1) {
471
+ const processedStatement = SQLRowLimiter.applyMaxRows(statements[0], options.maxRows);
472
+ let result;
473
+ if (parameters && parameters.length > 0) {
474
+ try {
475
+ result = await client.query(processedStatement, parameters);
476
+ } catch (error) {
477
+ console.error(`[PostgreSQL executeSQL] ERROR: ${error.message}`);
478
+ console.error(`[PostgreSQL executeSQL] SQL: ${processedStatement}`);
479
+ console.error(`[PostgreSQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
480
+ throw error;
481
+ }
482
+ } else {
483
+ result = await client.query(processedStatement);
484
+ }
485
+ return { rows: result.rows, rowCount: result.rowCount ?? result.rows.length };
486
+ } else {
487
+ if (parameters && parameters.length > 0) {
488
+ throw new Error("Parameters are not supported for multi-statement queries in PostgreSQL");
489
+ }
490
+ let allRows = [];
491
+ let totalRowCount = 0;
492
+ await client.query("BEGIN");
493
+ try {
494
+ for (let statement of statements) {
495
+ const processedStatement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
496
+ const result = await client.query(processedStatement);
497
+ if (result.rows && result.rows.length > 0) {
498
+ allRows.push(...result.rows);
499
+ }
500
+ if (result.rowCount) {
501
+ totalRowCount += result.rowCount;
502
+ }
503
+ }
504
+ await client.query("COMMIT");
505
+ } catch (error) {
506
+ await client.query("ROLLBACK");
507
+ throw error;
508
+ }
509
+ return { rows: allRows, rowCount: totalRowCount };
510
+ }
511
+ } finally {
512
+ client.release();
513
+ }
514
+ }
515
+ };
516
+ var postgresConnector = new PostgresConnector();
517
+ ConnectorRegistry.register(postgresConnector);
518
+ export {
519
+ PostgresConnector
520
+ };