@memberjunction/db-auto-doc 2.117.0 → 2.118.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 (251) hide show
  1. package/README.md +652 -165
  2. package/bin/run.js +7 -0
  3. package/dist/api/DBAutoDocAPI.d.ts +252 -0
  4. package/dist/api/DBAutoDocAPI.d.ts.map +1 -0
  5. package/dist/api/DBAutoDocAPI.js +530 -0
  6. package/dist/api/DBAutoDocAPI.js.map +1 -0
  7. package/dist/api/index.d.ts +7 -0
  8. package/dist/api/index.d.ts.map +1 -0
  9. package/dist/api/index.js +10 -0
  10. package/dist/api/index.js.map +1 -0
  11. package/dist/commands/analyze.d.ts +6 -4
  12. package/dist/commands/analyze.d.ts.map +1 -1
  13. package/dist/commands/analyze.js +58 -71
  14. package/dist/commands/analyze.js.map +1 -1
  15. package/dist/commands/export.d.ts +14 -4
  16. package/dist/commands/export.d.ts.map +1 -1
  17. package/dist/commands/export.js +156 -61
  18. package/dist/commands/export.js.map +1 -1
  19. package/dist/commands/init.d.ts +3 -4
  20. package/dist/commands/init.d.ts.map +1 -1
  21. package/dist/commands/init.js +155 -146
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/reset.d.ts +4 -1
  24. package/dist/commands/reset.d.ts.map +1 -1
  25. package/dist/commands/reset.js +33 -19
  26. package/dist/commands/reset.js.map +1 -1
  27. package/dist/commands/status.d.ts +10 -0
  28. package/dist/commands/status.d.ts.map +1 -0
  29. package/dist/commands/status.js +66 -0
  30. package/dist/commands/status.js.map +1 -0
  31. package/dist/core/AnalysisEngine.d.ts +108 -0
  32. package/dist/core/AnalysisEngine.d.ts.map +1 -0
  33. package/dist/core/AnalysisEngine.js +716 -0
  34. package/dist/core/AnalysisEngine.js.map +1 -0
  35. package/dist/core/AnalysisOrchestrator.d.ts +37 -0
  36. package/dist/core/AnalysisOrchestrator.d.ts.map +1 -0
  37. package/dist/core/AnalysisOrchestrator.js +294 -0
  38. package/dist/core/AnalysisOrchestrator.js.map +1 -0
  39. package/dist/core/BackpropagationEngine.d.ts +32 -0
  40. package/dist/core/BackpropagationEngine.d.ts.map +1 -0
  41. package/dist/core/BackpropagationEngine.js +121 -0
  42. package/dist/core/BackpropagationEngine.js.map +1 -0
  43. package/dist/core/ConvergenceDetector.d.ts +27 -0
  44. package/dist/core/ConvergenceDetector.d.ts.map +1 -0
  45. package/dist/core/ConvergenceDetector.js +92 -0
  46. package/dist/core/ConvergenceDetector.js.map +1 -0
  47. package/dist/core/GuardrailsManager.d.ts +78 -0
  48. package/dist/core/GuardrailsManager.d.ts.map +1 -0
  49. package/dist/core/GuardrailsManager.js +367 -0
  50. package/dist/core/GuardrailsManager.js.map +1 -0
  51. package/dist/core/index.d.ts +7 -0
  52. package/dist/core/index.d.ts.map +1 -0
  53. package/dist/core/index.js +13 -0
  54. package/dist/core/index.js.map +1 -0
  55. package/dist/database/Database.d.ts +56 -0
  56. package/dist/database/Database.d.ts.map +1 -0
  57. package/dist/database/Database.js +172 -0
  58. package/dist/database/Database.js.map +1 -0
  59. package/dist/database/TopologicalSorter.d.ts +25 -0
  60. package/dist/database/TopologicalSorter.d.ts.map +1 -0
  61. package/dist/database/TopologicalSorter.js +150 -0
  62. package/dist/database/TopologicalSorter.js.map +1 -0
  63. package/dist/database/index.d.ts +6 -0
  64. package/dist/database/index.d.ts.map +1 -0
  65. package/dist/database/index.js +14 -0
  66. package/dist/database/index.js.map +1 -0
  67. package/dist/discovery/ColumnStatsCache.d.ts +91 -0
  68. package/dist/discovery/ColumnStatsCache.d.ts.map +1 -0
  69. package/dist/discovery/ColumnStatsCache.js +231 -0
  70. package/dist/discovery/ColumnStatsCache.js.map +1 -0
  71. package/dist/discovery/DiscoveryEngine.d.ts +100 -0
  72. package/dist/discovery/DiscoveryEngine.d.ts.map +1 -0
  73. package/dist/discovery/DiscoveryEngine.js +726 -0
  74. package/dist/discovery/DiscoveryEngine.js.map +1 -0
  75. package/dist/discovery/DiscoveryTriggerAnalyzer.d.ts +57 -0
  76. package/dist/discovery/DiscoveryTriggerAnalyzer.d.ts.map +1 -0
  77. package/dist/discovery/DiscoveryTriggerAnalyzer.js +186 -0
  78. package/dist/discovery/DiscoveryTriggerAnalyzer.js.map +1 -0
  79. package/dist/discovery/FKDetector.d.ts +47 -0
  80. package/dist/discovery/FKDetector.d.ts.map +1 -0
  81. package/dist/discovery/FKDetector.js +317 -0
  82. package/dist/discovery/FKDetector.js.map +1 -0
  83. package/dist/discovery/LLMDiscoveryValidator.d.ts +64 -0
  84. package/dist/discovery/LLMDiscoveryValidator.d.ts.map +1 -0
  85. package/dist/discovery/LLMDiscoveryValidator.js +431 -0
  86. package/dist/discovery/LLMDiscoveryValidator.js.map +1 -0
  87. package/dist/discovery/LLMSanityChecker.d.ts +38 -0
  88. package/dist/discovery/LLMSanityChecker.d.ts.map +1 -0
  89. package/dist/discovery/LLMSanityChecker.js +156 -0
  90. package/dist/discovery/LLMSanityChecker.js.map +1 -0
  91. package/dist/discovery/PKDetector.d.ts +62 -0
  92. package/dist/discovery/PKDetector.d.ts.map +1 -0
  93. package/dist/discovery/PKDetector.js +436 -0
  94. package/dist/discovery/PKDetector.js.map +1 -0
  95. package/dist/discovery/index.d.ts +9 -0
  96. package/dist/discovery/index.d.ts.map +1 -0
  97. package/dist/discovery/index.js +25 -0
  98. package/dist/discovery/index.js.map +1 -0
  99. package/dist/drivers/BaseAutoDocDriver.d.ts +132 -0
  100. package/dist/drivers/BaseAutoDocDriver.d.ts.map +1 -0
  101. package/dist/drivers/BaseAutoDocDriver.js +121 -0
  102. package/dist/drivers/BaseAutoDocDriver.js.map +1 -0
  103. package/dist/drivers/MySQLDriver.d.ts +61 -0
  104. package/dist/drivers/MySQLDriver.d.ts.map +1 -0
  105. package/dist/drivers/MySQLDriver.js +668 -0
  106. package/dist/drivers/MySQLDriver.js.map +1 -0
  107. package/dist/drivers/PostgreSQLDriver.d.ts +65 -0
  108. package/dist/drivers/PostgreSQLDriver.d.ts.map +1 -0
  109. package/dist/drivers/PostgreSQLDriver.js +704 -0
  110. package/dist/drivers/PostgreSQLDriver.js.map +1 -0
  111. package/dist/drivers/SQLServerDriver.d.ts +61 -0
  112. package/dist/drivers/SQLServerDriver.d.ts.map +1 -0
  113. package/dist/drivers/SQLServerDriver.js +667 -0
  114. package/dist/drivers/SQLServerDriver.js.map +1 -0
  115. package/dist/generators/CSVGenerator.d.ts +35 -0
  116. package/dist/generators/CSVGenerator.d.ts.map +1 -0
  117. package/dist/generators/CSVGenerator.js +154 -0
  118. package/dist/generators/CSVGenerator.js.map +1 -0
  119. package/dist/generators/HTMLGenerator.d.ts +29 -0
  120. package/dist/generators/HTMLGenerator.d.ts.map +1 -0
  121. package/dist/generators/HTMLGenerator.js +710 -0
  122. package/dist/generators/HTMLGenerator.js.map +1 -0
  123. package/dist/generators/MarkdownGenerator.d.ts +27 -0
  124. package/dist/generators/MarkdownGenerator.d.ts.map +1 -0
  125. package/dist/generators/MarkdownGenerator.js +361 -0
  126. package/dist/generators/MarkdownGenerator.js.map +1 -0
  127. package/dist/generators/MermaidGenerator.d.ts +35 -0
  128. package/dist/generators/MermaidGenerator.d.ts.map +1 -0
  129. package/dist/generators/MermaidGenerator.js +321 -0
  130. package/dist/generators/MermaidGenerator.js.map +1 -0
  131. package/dist/generators/ReportGenerator.d.ts +22 -0
  132. package/dist/generators/ReportGenerator.d.ts.map +1 -0
  133. package/dist/generators/ReportGenerator.js +176 -0
  134. package/dist/generators/ReportGenerator.js.map +1 -0
  135. package/dist/generators/SQLGenerator.d.ts +31 -0
  136. package/dist/generators/SQLGenerator.d.ts.map +1 -0
  137. package/dist/generators/SQLGenerator.js +168 -0
  138. package/dist/generators/SQLGenerator.js.map +1 -0
  139. package/dist/generators/index.d.ts +10 -0
  140. package/dist/generators/index.d.ts.map +1 -0
  141. package/dist/generators/index.js +19 -0
  142. package/dist/generators/index.js.map +1 -0
  143. package/dist/index.d.ts +11 -20
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +19 -20
  146. package/dist/index.js.map +1 -1
  147. package/dist/prompts/PromptEngine.d.ts +65 -0
  148. package/dist/prompts/PromptEngine.d.ts.map +1 -0
  149. package/dist/prompts/PromptEngine.js +282 -0
  150. package/dist/prompts/PromptEngine.js.map +1 -0
  151. package/dist/prompts/PromptFileLoader.d.ts +21 -0
  152. package/dist/prompts/PromptFileLoader.d.ts.map +1 -0
  153. package/dist/prompts/PromptFileLoader.js +74 -0
  154. package/dist/prompts/PromptFileLoader.js.map +1 -0
  155. package/dist/prompts/index.d.ts +6 -0
  156. package/dist/prompts/index.d.ts.map +1 -0
  157. package/dist/prompts/index.js +11 -0
  158. package/dist/prompts/index.js.map +1 -0
  159. package/dist/state/IterationTracker.d.ts +64 -0
  160. package/dist/state/IterationTracker.d.ts.map +1 -0
  161. package/dist/state/IterationTracker.js +136 -0
  162. package/dist/state/IterationTracker.js.map +1 -0
  163. package/dist/state/StateManager.d.ts +79 -0
  164. package/dist/state/StateManager.d.ts.map +1 -0
  165. package/dist/state/StateManager.js +348 -0
  166. package/dist/state/StateManager.js.map +1 -0
  167. package/dist/state/StateValidator.d.ts +24 -0
  168. package/dist/state/StateValidator.d.ts.map +1 -0
  169. package/dist/state/StateValidator.js +147 -0
  170. package/dist/state/StateValidator.js.map +1 -0
  171. package/dist/state/index.d.ts +7 -0
  172. package/dist/state/index.d.ts.map +1 -0
  173. package/dist/state/index.js +13 -0
  174. package/dist/state/index.js.map +1 -0
  175. package/dist/types/analysis.d.ts +76 -0
  176. package/dist/types/analysis.d.ts.map +1 -0
  177. package/dist/types/analysis.js +6 -0
  178. package/dist/types/analysis.js.map +1 -0
  179. package/dist/types/config.d.ts +132 -0
  180. package/dist/types/config.d.ts.map +1 -0
  181. package/dist/types/config.js +7 -0
  182. package/dist/types/config.js.map +1 -0
  183. package/dist/types/discovery.d.ts +277 -0
  184. package/dist/types/discovery.d.ts.map +1 -0
  185. package/dist/types/discovery.js +7 -0
  186. package/dist/types/discovery.js.map +1 -0
  187. package/dist/types/driver.d.ts +148 -0
  188. package/dist/types/driver.d.ts.map +1 -0
  189. package/dist/types/driver.js +7 -0
  190. package/dist/types/driver.js.map +1 -0
  191. package/dist/types/index.d.ts +8 -0
  192. package/dist/types/index.d.ts.map +1 -0
  193. package/dist/types/index.js +24 -0
  194. package/dist/types/index.js.map +1 -0
  195. package/dist/types/prompts.d.ts +158 -0
  196. package/dist/types/prompts.d.ts.map +1 -0
  197. package/dist/types/prompts.js +6 -0
  198. package/dist/types/prompts.js.map +1 -0
  199. package/dist/types/state.d.ts +278 -0
  200. package/dist/types/state.d.ts.map +1 -0
  201. package/dist/types/state.js +7 -0
  202. package/dist/types/state.js.map +1 -0
  203. package/dist/utils/config-loader.d.ts +29 -0
  204. package/dist/utils/config-loader.d.ts.map +1 -0
  205. package/dist/utils/config-loader.js +163 -0
  206. package/dist/utils/config-loader.js.map +1 -0
  207. package/dist/utils/index.d.ts +5 -0
  208. package/dist/utils/index.d.ts.map +1 -0
  209. package/dist/utils/index.js +9 -0
  210. package/dist/utils/index.js.map +1 -0
  211. package/package.json +24 -3
  212. package/dist/ai/simple-ai-client.d.ts +0 -70
  213. package/dist/ai/simple-ai-client.d.ts.map +0 -1
  214. package/dist/ai/simple-ai-client.js +0 -181
  215. package/dist/ai/simple-ai-client.js.map +0 -1
  216. package/dist/analyzers/analyzer.d.ts +0 -23
  217. package/dist/analyzers/analyzer.d.ts.map +0 -1
  218. package/dist/analyzers/analyzer.js +0 -127
  219. package/dist/analyzers/analyzer.js.map +0 -1
  220. package/dist/cli-old/cli.d.ts +0 -3
  221. package/dist/cli-old/cli.d.ts.map +0 -1
  222. package/dist/cli-old/cli.js +0 -388
  223. package/dist/cli-old/cli.js.map +0 -1
  224. package/dist/commands/review.d.ts +0 -11
  225. package/dist/commands/review.d.ts.map +0 -1
  226. package/dist/commands/review.js +0 -82
  227. package/dist/commands/review.js.map +0 -1
  228. package/dist/database/connection.d.ts +0 -40
  229. package/dist/database/connection.d.ts.map +0 -1
  230. package/dist/database/connection.js +0 -136
  231. package/dist/database/connection.js.map +0 -1
  232. package/dist/database/introspection.d.ts +0 -59
  233. package/dist/database/introspection.d.ts.map +0 -1
  234. package/dist/database/introspection.js +0 -124
  235. package/dist/database/introspection.js.map +0 -1
  236. package/dist/generators/markdown-generator.d.ts +0 -8
  237. package/dist/generators/markdown-generator.d.ts.map +0 -1
  238. package/dist/generators/markdown-generator.js +0 -106
  239. package/dist/generators/markdown-generator.js.map +0 -1
  240. package/dist/generators/sql-generator.d.ts +0 -20
  241. package/dist/generators/sql-generator.d.ts.map +0 -1
  242. package/dist/generators/sql-generator.js +0 -83
  243. package/dist/generators/sql-generator.js.map +0 -1
  244. package/dist/state/state-manager.d.ts +0 -95
  245. package/dist/state/state-manager.d.ts.map +0 -1
  246. package/dist/state/state-manager.js +0 -236
  247. package/dist/state/state-manager.js.map +0 -1
  248. package/dist/types/state-file.d.ts +0 -124
  249. package/dist/types/state-file.d.ts.map +0 -1
  250. package/dist/types/state-file.js +0 -79
  251. package/dist/types/state-file.js.map +0 -1
@@ -0,0 +1,704 @@
1
+ "use strict";
2
+ /**
3
+ * PostgreSQL implementation of the BaseAutoDocDriver
4
+ * Uses pg driver for database connectivity
5
+ */
6
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
7
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
8
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
9
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ var __metadata = (this && this.__metadata) || function (k, v) {
13
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.PostgreSQLDriver = void 0;
17
+ const pg_1 = require("pg");
18
+ const global_1 = require("@memberjunction/global");
19
+ const BaseAutoDocDriver_js_1 = require("./BaseAutoDocDriver.js");
20
+ /**
21
+ * PostgreSQL driver implementation
22
+ * Registered with MJGlobal for factory instantiation
23
+ */
24
+ let PostgreSQLDriver = class PostgreSQLDriver extends BaseAutoDocDriver_js_1.BaseAutoDocDriver {
25
+ constructor(config) {
26
+ super(config);
27
+ this.pool = null;
28
+ // Map generic config to PostgreSQL specific config
29
+ this.pgConfig = {
30
+ host: config.host,
31
+ port: config.port || 5432,
32
+ database: config.database,
33
+ user: config.user || config.username,
34
+ password: config.password,
35
+ ssl: config.ssl,
36
+ connectionTimeoutMillis: config.connectionTimeout ?? 30000,
37
+ max: config.maxConnections ?? 10,
38
+ min: config.minConnections ?? 0,
39
+ idleTimeoutMillis: config.idleTimeoutMillis ?? 30000
40
+ };
41
+ }
42
+ // ============================================================================
43
+ // CONNECTION MANAGEMENT
44
+ // ============================================================================
45
+ async connect() {
46
+ if (this.pool) {
47
+ return;
48
+ }
49
+ this.pool = new pg_1.Pool(this.pgConfig);
50
+ }
51
+ async test() {
52
+ try {
53
+ await this.connect();
54
+ const result = await this.executeQuery(`
55
+ SELECT
56
+ 1 as test,
57
+ version() as version,
58
+ current_database() as db
59
+ `);
60
+ if (result.success && result.data && result.data.length > 0) {
61
+ return {
62
+ success: true,
63
+ message: `Successfully connected to ${result.data[0].db} on ${this.config.host}`,
64
+ serverVersion: result.data[0].version,
65
+ databaseName: result.data[0].db
66
+ };
67
+ }
68
+ return {
69
+ success: false,
70
+ message: 'Connection established but test query failed'
71
+ };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ success: false,
76
+ message: `Connection failed: ${error.message}`
77
+ };
78
+ }
79
+ }
80
+ async close() {
81
+ if (this.pool) {
82
+ await this.pool.end();
83
+ this.pool = null;
84
+ }
85
+ }
86
+ // ============================================================================
87
+ // SCHEMA INTROSPECTION
88
+ // ============================================================================
89
+ async getSchemas(schemaFilter, tableFilter) {
90
+ // Get all tables grouped by schema
91
+ const tablesQuery = this.buildTablesQuery(schemaFilter, tableFilter);
92
+ const tablesResult = await this.executeQuery(tablesQuery);
93
+ if (!tablesResult.success || !tablesResult.data) {
94
+ throw new Error(`Failed to get tables: ${tablesResult.errorMessage}`);
95
+ }
96
+ // Group tables by schema
97
+ const schemaMap = new Map();
98
+ for (const row of tablesResult.data) {
99
+ if (!schemaMap.has(row.schema_name)) {
100
+ schemaMap.set(row.schema_name, []);
101
+ }
102
+ schemaMap.get(row.schema_name).push({
103
+ tableName: row.table_name,
104
+ rowCount: row.row_count
105
+ });
106
+ }
107
+ // Build schemas with full table details
108
+ const schemas = [];
109
+ for (const [schemaName, tableSummaries] of schemaMap) {
110
+ const tables = [];
111
+ for (const { tableName, rowCount } of tableSummaries) {
112
+ const [columns, foreignKeys, primaryKeys] = await Promise.all([
113
+ this.getColumns(schemaName, tableName),
114
+ this.getForeignKeys(schemaName, tableName),
115
+ this.getPrimaryKeys(schemaName, tableName)
116
+ ]);
117
+ tables.push({
118
+ schemaName,
119
+ tableName,
120
+ rowCount,
121
+ columns,
122
+ foreignKeys,
123
+ primaryKeys
124
+ });
125
+ }
126
+ schemas.push({
127
+ name: schemaName,
128
+ tables
129
+ });
130
+ }
131
+ return schemas;
132
+ }
133
+ async getTables(schemaName, tableFilter) {
134
+ const query = this.buildTablesQuery({ include: [schemaName] }, tableFilter);
135
+ const result = await this.executeQuery(query);
136
+ if (!result.success || !result.data) {
137
+ return [];
138
+ }
139
+ const tables = [];
140
+ for (const row of result.data) {
141
+ const [columns, foreignKeys, primaryKeys] = await Promise.all([
142
+ this.getColumns(row.schema_name, row.table_name),
143
+ this.getForeignKeys(row.schema_name, row.table_name),
144
+ this.getPrimaryKeys(row.schema_name, row.table_name)
145
+ ]);
146
+ tables.push({
147
+ schemaName: row.schema_name,
148
+ tableName: row.table_name,
149
+ rowCount: row.row_count,
150
+ columns,
151
+ foreignKeys,
152
+ primaryKeys
153
+ });
154
+ }
155
+ return tables;
156
+ }
157
+ async getColumns(schemaName, tableName) {
158
+ const query = `
159
+ SELECT
160
+ c.column_name,
161
+ c.data_type,
162
+ c.udt_name,
163
+ CASE WHEN c.is_nullable = 'YES' THEN true ELSE false END as is_nullable,
164
+ c.character_maximum_length,
165
+ c.numeric_precision,
166
+ c.numeric_scale,
167
+ c.column_default,
168
+ CASE WHEN pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key,
169
+ CASE WHEN fk.column_name IS NOT NULL THEN true ELSE false END as is_foreign_key,
170
+ cc.check_clause as check_constraint
171
+ FROM information_schema.columns c
172
+ LEFT JOIN (
173
+ SELECT
174
+ kcu.table_schema,
175
+ kcu.table_name,
176
+ kcu.column_name
177
+ FROM information_schema.table_constraints tc
178
+ JOIN information_schema.key_column_usage kcu
179
+ ON tc.constraint_name = kcu.constraint_name
180
+ AND tc.table_schema = kcu.table_schema
181
+ WHERE tc.constraint_type = 'PRIMARY KEY'
182
+ ) pk ON c.table_schema = pk.table_schema
183
+ AND c.table_name = pk.table_name
184
+ AND c.column_name = pk.column_name
185
+ LEFT JOIN (
186
+ SELECT
187
+ kcu.table_schema,
188
+ kcu.table_name,
189
+ kcu.column_name
190
+ FROM information_schema.table_constraints tc
191
+ JOIN information_schema.key_column_usage kcu
192
+ ON tc.constraint_name = kcu.constraint_name
193
+ AND tc.table_schema = kcu.table_schema
194
+ WHERE tc.constraint_type = 'FOREIGN KEY'
195
+ ) fk ON c.table_schema = fk.table_schema
196
+ AND c.table_name = fk.table_name
197
+ AND c.column_name = fk.column_name
198
+ LEFT JOIN (
199
+ SELECT
200
+ ccu.table_schema,
201
+ ccu.table_name,
202
+ ccu.column_name,
203
+ cc.check_clause
204
+ FROM information_schema.check_constraints cc
205
+ JOIN information_schema.constraint_column_usage ccu
206
+ ON cc.constraint_name = ccu.constraint_name
207
+ AND cc.constraint_schema = ccu.constraint_schema
208
+ ) cc ON c.table_schema = cc.table_schema
209
+ AND c.table_name = cc.table_name
210
+ AND c.column_name = cc.column_name
211
+ WHERE c.table_schema = $1 AND c.table_name = $2
212
+ ORDER BY c.ordinal_position
213
+ `;
214
+ const result = await this.executeQuery(query, 3, [schemaName, tableName]);
215
+ if (!result.success || !result.data) {
216
+ return [];
217
+ }
218
+ return result.data.map(row => ({
219
+ name: row.column_name,
220
+ dataType: this.normalizeDataType(row.data_type, row.udt_name),
221
+ isNullable: row.is_nullable,
222
+ isPrimaryKey: row.is_primary_key,
223
+ isForeignKey: row.is_foreign_key,
224
+ checkConstraint: row.check_constraint || undefined,
225
+ defaultValue: row.column_default || undefined,
226
+ maxLength: row.character_maximum_length || undefined,
227
+ precision: row.numeric_precision || undefined,
228
+ scale: row.numeric_scale || undefined
229
+ }));
230
+ }
231
+ async getExistingDescriptions(schemaName, tableName) {
232
+ // PostgreSQL uses pg_description for comments
233
+ const query = `
234
+ SELECT
235
+ COALESCE(a.attname, '') as column_name,
236
+ d.description
237
+ FROM pg_description d
238
+ JOIN pg_class c ON d.objoid = c.oid
239
+ JOIN pg_namespace n ON c.relnamespace = n.oid
240
+ LEFT JOIN pg_attribute a ON d.objoid = a.attrelid AND d.objsubid = a.attnum
241
+ WHERE n.nspname = $1
242
+ AND c.relname = $2
243
+ AND d.description IS NOT NULL
244
+ `;
245
+ const result = await this.executeQuery(query, 3, [schemaName, tableName]);
246
+ if (!result.success || !result.data) {
247
+ return [];
248
+ }
249
+ return result.data.map(row => ({
250
+ target: row.column_name ? 'column' : 'table',
251
+ targetName: row.column_name,
252
+ description: row.description
253
+ }));
254
+ }
255
+ // ============================================================================
256
+ // DATA SAMPLING AND STATISTICS
257
+ // ============================================================================
258
+ async getColumnStatistics(schemaName, tableName, columnName, dataType, cardinalityThreshold, sampleSize) {
259
+ const stats = {
260
+ totalRows: 0,
261
+ distinctCount: 0,
262
+ uniquenessRatio: 0,
263
+ nullCount: 0,
264
+ nullPercentage: 0,
265
+ sampleValues: []
266
+ };
267
+ // Get cardinality and null statistics
268
+ const cardinalityStats = await this.getCardinalityStats(schemaName, tableName, columnName);
269
+ Object.assign(stats, cardinalityStats);
270
+ // Get value distribution for low-cardinality columns
271
+ if (stats.distinctCount <= cardinalityThreshold && stats.distinctCount > 0) {
272
+ const distribution = await this.getValueDistribution(schemaName, tableName, columnName, 100);
273
+ stats.valueDistribution = distribution.map(d => ({
274
+ ...d,
275
+ percentage: (d.frequency / (cardinalityStats.totalCount || 1)) * 100
276
+ }));
277
+ }
278
+ // Get type-specific statistics
279
+ if (this.isNumericType(dataType)) {
280
+ const numericStats = await this.getNumericStats(schemaName, tableName, columnName);
281
+ Object.assign(stats, numericStats);
282
+ }
283
+ else if (this.isDateType(dataType)) {
284
+ const dateStats = await this.getDateStats(schemaName, tableName, columnName);
285
+ Object.assign(stats, dateStats);
286
+ }
287
+ else if (this.isStringType(dataType)) {
288
+ const stringStats = await this.getStringStats(schemaName, tableName, columnName);
289
+ Object.assign(stats, stringStats);
290
+ }
291
+ // Get sample values
292
+ stats.sampleValues = await this.getSampleValues(schemaName, tableName, columnName, sampleSize);
293
+ return stats;
294
+ }
295
+ async getDistinctCount(schemaName, tableName, columnName) {
296
+ const query = `
297
+ SELECT COUNT(DISTINCT ${this.escapeIdentifier(columnName)}) as distinct_count
298
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
299
+ `;
300
+ const result = await this.executeQuery(query);
301
+ return result.success && result.data && result.data.length > 0 ? Number(result.data[0].distinct_count) : 0;
302
+ }
303
+ async getValueDistribution(schemaName, tableName, columnName, limit) {
304
+ const query = `
305
+ SELECT
306
+ ${this.escapeIdentifier(columnName)} as value,
307
+ COUNT(*) as frequency
308
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
309
+ WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
310
+ GROUP BY ${this.escapeIdentifier(columnName)}
311
+ ORDER BY COUNT(*) DESC
312
+ LIMIT ${limit}
313
+ `;
314
+ const result = await this.executeQuery(query);
315
+ return result.success && result.data ? result.data.map(r => ({
316
+ value: r.value,
317
+ frequency: Number(r.frequency)
318
+ })) : [];
319
+ }
320
+ async getSampleValues(schemaName, tableName, columnName, sampleSize) {
321
+ // Limit sample size to max 20 to reduce JSON size
322
+ const limitedSampleSize = Math.min(sampleSize, 20);
323
+ const query = `
324
+ SELECT ${this.escapeIdentifier(columnName)} as value
325
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
326
+ WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
327
+ ORDER BY RANDOM()
328
+ LIMIT ${limitedSampleSize}
329
+ `;
330
+ const result = await this.executeQuery(query);
331
+ return result.success && result.data ? result.data.map(r => r.value) : [];
332
+ }
333
+ // ============================================================================
334
+ // QUERY EXECUTION
335
+ // ============================================================================
336
+ async executeQuery(query, maxRetries = 3, params) {
337
+ let lastError = null;
338
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
339
+ try {
340
+ if (!this.pool) {
341
+ await this.connect();
342
+ }
343
+ const result = params
344
+ ? await this.pool.query(query, params)
345
+ : await this.pool.query(query);
346
+ return {
347
+ success: true,
348
+ data: result.rows,
349
+ rowCount: result.rowCount || 0
350
+ };
351
+ }
352
+ catch (error) {
353
+ lastError = error;
354
+ // Check if error is transient
355
+ if (this.isTransientError(error) && attempt < maxRetries) {
356
+ await this.sleep(Math.pow(2, attempt) * 1000);
357
+ continue;
358
+ }
359
+ break;
360
+ }
361
+ }
362
+ return {
363
+ success: false,
364
+ errorMessage: lastError?.message || 'Unknown error'
365
+ };
366
+ }
367
+ // ============================================================================
368
+ // PROVIDER-SPECIFIC HELPERS
369
+ // ============================================================================
370
+ escapeIdentifier(identifier) {
371
+ return `"${identifier}"`;
372
+ }
373
+ getLimitClause(limit) {
374
+ return `LIMIT ${limit}`;
375
+ }
376
+ // ============================================================================
377
+ // PRIVATE HELPER METHODS
378
+ // ============================================================================
379
+ buildTablesQuery(schemaFilter, tableFilter) {
380
+ let whereClause = "WHERE t.table_type = 'BASE TABLE'";
381
+ whereClause += this.buildSchemaFilterClause(schemaFilter, 't.table_schema');
382
+ whereClause += this.buildTableFilterClause(tableFilter, 't.table_name');
383
+ // Exclude system schemas by default
384
+ if (!schemaFilter.include || schemaFilter.include.length === 0) {
385
+ whereClause += ` AND t.table_schema NOT IN ('pg_catalog', 'information_schema')`;
386
+ }
387
+ return `
388
+ SELECT
389
+ t.table_schema as schema_name,
390
+ t.table_name as table_name,
391
+ COALESCE(s.n_live_tup, 0) as row_count
392
+ FROM information_schema.tables t
393
+ LEFT JOIN pg_stat_user_tables s
394
+ ON t.table_schema = s.schemaname
395
+ AND t.table_name = s.relname
396
+ ${whereClause}
397
+ ORDER BY t.table_schema, t.table_name
398
+ `;
399
+ }
400
+ async getForeignKeys(schemaName, tableName) {
401
+ const query = `
402
+ SELECT
403
+ kcu.column_name,
404
+ ccu.table_schema as referenced_schema,
405
+ ccu.table_name as referenced_table,
406
+ ccu.column_name as referenced_column,
407
+ tc.constraint_name
408
+ FROM information_schema.table_constraints tc
409
+ JOIN information_schema.key_column_usage kcu
410
+ ON tc.constraint_name = kcu.constraint_name
411
+ AND tc.table_schema = kcu.table_schema
412
+ JOIN information_schema.constraint_column_usage ccu
413
+ ON ccu.constraint_name = tc.constraint_name
414
+ AND ccu.table_schema = tc.table_schema
415
+ WHERE tc.constraint_type = 'FOREIGN KEY'
416
+ AND tc.table_schema = $1
417
+ AND tc.table_name = $2
418
+ `;
419
+ const result = await this.executeQuery(query, 3, [schemaName, tableName]);
420
+ if (!result.success || !result.data) {
421
+ return [];
422
+ }
423
+ return result.data.map(row => ({
424
+ columnName: row.column_name,
425
+ referencedSchema: row.referenced_schema,
426
+ referencedTable: row.referenced_table,
427
+ referencedColumn: row.referenced_column,
428
+ constraintName: row.constraint_name
429
+ }));
430
+ }
431
+ async getPrimaryKeys(schemaName, tableName) {
432
+ const query = `
433
+ SELECT
434
+ kcu.column_name,
435
+ kcu.ordinal_position,
436
+ tc.constraint_name
437
+ FROM information_schema.table_constraints tc
438
+ JOIN information_schema.key_column_usage kcu
439
+ ON tc.constraint_name = kcu.constraint_name
440
+ AND tc.table_schema = kcu.table_schema
441
+ WHERE tc.constraint_type = 'PRIMARY KEY'
442
+ AND tc.table_schema = $1
443
+ AND tc.table_name = $2
444
+ ORDER BY kcu.ordinal_position
445
+ `;
446
+ const result = await this.executeQuery(query, 3, [schemaName, tableName]);
447
+ if (!result.success || !result.data) {
448
+ return [];
449
+ }
450
+ return result.data.map(row => ({
451
+ columnName: row.column_name,
452
+ ordinalPosition: row.ordinal_position,
453
+ constraintName: row.constraint_name
454
+ }));
455
+ }
456
+ async getCardinalityStats(schemaName, tableName, columnName) {
457
+ const query = `
458
+ SELECT
459
+ COUNT(DISTINCT ${this.escapeIdentifier(columnName)}) as distinct_count,
460
+ COUNT(*) as total_count,
461
+ COUNT(*) - COUNT(${this.escapeIdentifier(columnName)}) as null_count
462
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
463
+ `;
464
+ const result = await this.executeQuery(query);
465
+ if (result.success && result.data && result.data.length > 0) {
466
+ const row = result.data[0];
467
+ const totalCount = Number(row.total_count) || 1;
468
+ const distinctCount = Number(row.distinct_count);
469
+ const nullCount = Number(row.null_count);
470
+ return {
471
+ distinctCount,
472
+ uniquenessRatio: distinctCount / totalCount,
473
+ nullCount,
474
+ nullPercentage: (nullCount / totalCount) * 100,
475
+ totalCount
476
+ };
477
+ }
478
+ return {
479
+ distinctCount: 0,
480
+ uniquenessRatio: 0,
481
+ nullCount: 0,
482
+ nullPercentage: 0,
483
+ totalCount: 0
484
+ };
485
+ }
486
+ async getNumericStats(schemaName, tableName, columnName) {
487
+ const query = `
488
+ SELECT
489
+ MIN(${this.escapeIdentifier(columnName)}) as min_value,
490
+ MAX(${this.escapeIdentifier(columnName)}) as max_value,
491
+ AVG(${this.escapeIdentifier(columnName)}::NUMERIC) as avg_value,
492
+ STDDEV(${this.escapeIdentifier(columnName)}) as std_dev
493
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
494
+ WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
495
+ `;
496
+ const result = await this.executeQuery(query);
497
+ if (result.success && result.data && result.data.length > 0) {
498
+ const row = result.data[0];
499
+ return {
500
+ min: row.min_value,
501
+ max: row.max_value,
502
+ avg: row.avg_value ? Number(row.avg_value) : undefined,
503
+ stdDev: row.std_dev ? Number(row.std_dev) : undefined
504
+ };
505
+ }
506
+ return {};
507
+ }
508
+ async getDateStats(schemaName, tableName, columnName) {
509
+ const query = `
510
+ SELECT
511
+ MIN(${this.escapeIdentifier(columnName)}) as min_value,
512
+ MAX(${this.escapeIdentifier(columnName)}) as max_value
513
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
514
+ WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
515
+ `;
516
+ const result = await this.executeQuery(query);
517
+ if (result.success && result.data && result.data.length > 0) {
518
+ const row = result.data[0];
519
+ return {
520
+ min: row.min_value,
521
+ max: row.max_value
522
+ };
523
+ }
524
+ return {};
525
+ }
526
+ async getStringStats(schemaName, tableName, columnName) {
527
+ const query = `
528
+ SELECT
529
+ AVG(LENGTH(${this.escapeIdentifier(columnName)})) as avg_length,
530
+ MAX(LENGTH(${this.escapeIdentifier(columnName)})) as max_length,
531
+ MIN(LENGTH(${this.escapeIdentifier(columnName)})) as min_length
532
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
533
+ WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
534
+ `;
535
+ const result = await this.executeQuery(query);
536
+ if (result.success && result.data && result.data.length > 0) {
537
+ const row = result.data[0];
538
+ return {
539
+ avgLength: row.avg_length ? Number(row.avg_length) : undefined,
540
+ maxLength: row.max_length,
541
+ minLength: row.min_length
542
+ };
543
+ }
544
+ return {};
545
+ }
546
+ isTransientError(error) {
547
+ const transientMessages = [
548
+ 'connection',
549
+ 'timeout',
550
+ 'deadlock',
551
+ 'network',
552
+ 'ECONNREFUSED',
553
+ 'ENOTFOUND',
554
+ 'ETIMEDOUT'
555
+ ];
556
+ const message = error.message.toLowerCase();
557
+ return transientMessages.some(msg => message.includes(msg.toLowerCase()));
558
+ }
559
+ /**
560
+ * Normalize PostgreSQL data types to generic format
561
+ */
562
+ normalizeDataType(dataType, udtName) {
563
+ // Map PostgreSQL types to more generic names
564
+ const typeMap = {
565
+ 'character varying': 'varchar',
566
+ 'character': 'char',
567
+ 'timestamp without time zone': 'timestamp',
568
+ 'timestamp with time zone': 'timestamptz',
569
+ 'time without time zone': 'time',
570
+ 'time with time zone': 'timetz',
571
+ 'double precision': 'float8',
572
+ 'integer': 'int4',
573
+ 'bigint': 'int8',
574
+ 'smallint': 'int2'
575
+ };
576
+ return typeMap[dataType.toLowerCase()] || udtName || dataType;
577
+ }
578
+ // ============================================================================
579
+ // RELATIONSHIP DISCOVERY METHODS
580
+ // ============================================================================
581
+ /**
582
+ * Get column information for relationship discovery
583
+ */
584
+ async getColumnInfo(schemaName, tableName, columnName) {
585
+ const query = `
586
+ SELECT
587
+ column_name as name,
588
+ data_type as type,
589
+ CASE WHEN is_nullable = 'YES' THEN true ELSE false END as nullable
590
+ FROM information_schema.columns
591
+ WHERE table_schema = $1
592
+ AND table_name = $2
593
+ AND column_name = $3
594
+ `;
595
+ const result = await this.executeQuery(query, 3, [schemaName, tableName, columnName]);
596
+ if (!result.success || !result.data || result.data.length === 0) {
597
+ throw new Error(`Column ${schemaName}.${tableName}.${columnName} not found`);
598
+ }
599
+ return {
600
+ name: result.data[0].name,
601
+ type: result.data[0].type,
602
+ nullable: result.data[0].nullable
603
+ };
604
+ }
605
+ /**
606
+ * Test value overlap between source and target columns
607
+ */
608
+ async testValueOverlap(sourceTable, sourceColumn, targetTable, targetColumn, sampleSize) {
609
+ try {
610
+ const [sourceSchema, sourceTableName] = this.parseTableIdentifier(sourceTable);
611
+ const [targetSchema, targetTableName] = this.parseTableIdentifier(targetTable);
612
+ const query = `
613
+ WITH source_sample AS (
614
+ SELECT DISTINCT ${this.escapeIdentifier(sourceColumn)} as value
615
+ FROM ${this.escapeIdentifier(sourceSchema)}.${this.escapeIdentifier(sourceTableName)}
616
+ WHERE ${this.escapeIdentifier(sourceColumn)} IS NOT NULL
617
+ ORDER BY RANDOM()
618
+ LIMIT ${sampleSize}
619
+ ),
620
+ target_values AS (
621
+ SELECT DISTINCT ${this.escapeIdentifier(targetColumn)} as value
622
+ FROM ${this.escapeIdentifier(targetSchema)}.${this.escapeIdentifier(targetTableName)}
623
+ WHERE ${this.escapeIdentifier(targetColumn)} IS NOT NULL
624
+ )
625
+ SELECT
626
+ COUNT(*) as total_source,
627
+ SUM(CASE WHEN tv.value IS NOT NULL THEN 1 ELSE 0 END) as matching_count
628
+ FROM source_sample ss
629
+ LEFT JOIN target_values tv ON ss.value = tv.value
630
+ `;
631
+ const result = await this.executeQuery(query);
632
+ if (!result.success || !result.data || result.data.length === 0) {
633
+ return 0;
634
+ }
635
+ const row = result.data[0];
636
+ const totalSource = Number(row.total_source);
637
+ const matchingCount = Number(row.matching_count);
638
+ if (totalSource === 0) {
639
+ return 0;
640
+ }
641
+ return matchingCount / totalSource;
642
+ }
643
+ catch (error) {
644
+ return 0;
645
+ }
646
+ }
647
+ /**
648
+ * Check if combination of columns is unique
649
+ */
650
+ async checkColumnCombinationUniqueness(schemaName, tableName, columnNames, sampleSize) {
651
+ try {
652
+ if (columnNames.length === 0) {
653
+ return false;
654
+ }
655
+ const escapedColumns = columnNames.map(col => this.escapeIdentifier(col));
656
+ const columnList = escapedColumns.join(', ');
657
+ const whereClause = escapedColumns.map(col => `${col} IS NOT NULL`).join(' AND ');
658
+ const query = `
659
+ WITH sampled_data AS (
660
+ SELECT
661
+ ${columnList}
662
+ FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
663
+ WHERE ${whereClause}
664
+ ORDER BY RANDOM()
665
+ LIMIT ${sampleSize}
666
+ ),
667
+ grouped_data AS (
668
+ SELECT
669
+ ${columnList},
670
+ COUNT(*) as occurrence_count
671
+ FROM sampled_data
672
+ GROUP BY ${columnList}
673
+ HAVING COUNT(*) > 1
674
+ )
675
+ SELECT COUNT(*) as duplicate_count
676
+ FROM grouped_data
677
+ `;
678
+ const result = await this.executeQuery(query);
679
+ if (!result.success || !result.data || result.data.length === 0) {
680
+ return false;
681
+ }
682
+ return Number(result.data[0].duplicate_count) === 0;
683
+ }
684
+ catch (error) {
685
+ return false;
686
+ }
687
+ }
688
+ /**
689
+ * Parse table identifier in format "schema.table"
690
+ */
691
+ parseTableIdentifier(tableIdentifier) {
692
+ const parts = tableIdentifier.split('.');
693
+ if (parts.length !== 2) {
694
+ throw new Error(`Invalid table identifier format: ${tableIdentifier}. Expected "schema.table"`);
695
+ }
696
+ return [parts[0], parts[1]];
697
+ }
698
+ };
699
+ exports.PostgreSQLDriver = PostgreSQLDriver;
700
+ exports.PostgreSQLDriver = PostgreSQLDriver = __decorate([
701
+ (0, global_1.RegisterClass)(BaseAutoDocDriver_js_1.BaseAutoDocDriver, 'PostgreSQL'),
702
+ __metadata("design:paramtypes", [Object])
703
+ ], PostgreSQLDriver);
704
+ //# sourceMappingURL=PostgreSQLDriver.js.map