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