@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.
- package/README.md +652 -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/init.d.ts +3 -4
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +155 -146
- 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 +37 -0
- package/dist/core/AnalysisOrchestrator.d.ts.map +1 -0
- package/dist/core/AnalysisOrchestrator.js +294 -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/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 +282 -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 +132 -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/state.d.ts +278 -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 +24 -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,667 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SQL Server implementation of the BaseAutoDocDriver
|
|
4
|
+
* Uses mssql driver for database connectivity
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
23
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
+
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;
|
|
26
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
+
};
|
|
28
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
36
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SQLServerDriver = void 0;
|
|
40
|
+
const sql = __importStar(require("mssql"));
|
|
41
|
+
const global_1 = require("@memberjunction/global");
|
|
42
|
+
const BaseAutoDocDriver_js_1 = require("./BaseAutoDocDriver.js");
|
|
43
|
+
/**
|
|
44
|
+
* SQL Server driver implementation
|
|
45
|
+
* Registered with MJGlobal for factory instantiation
|
|
46
|
+
*/
|
|
47
|
+
let SQLServerDriver = class SQLServerDriver extends BaseAutoDocDriver_js_1.BaseAutoDocDriver {
|
|
48
|
+
constructor(config) {
|
|
49
|
+
super(config);
|
|
50
|
+
this.pool = null;
|
|
51
|
+
// Map generic config to SQL Server specific config
|
|
52
|
+
this.sqlConfig = {
|
|
53
|
+
server: config.host,
|
|
54
|
+
port: config.port || 1433,
|
|
55
|
+
database: config.database,
|
|
56
|
+
user: config.user || config.username,
|
|
57
|
+
password: config.password,
|
|
58
|
+
options: {
|
|
59
|
+
encrypt: config.encrypt ?? true,
|
|
60
|
+
trustServerCertificate: config.trustServerCertificate ?? false
|
|
61
|
+
},
|
|
62
|
+
connectionTimeout: config.connectionTimeout ?? 30000,
|
|
63
|
+
requestTimeout: config.requestTimeout ?? 30000,
|
|
64
|
+
pool: {
|
|
65
|
+
max: config.maxConnections ?? 10,
|
|
66
|
+
min: config.minConnections ?? 0,
|
|
67
|
+
idleTimeoutMillis: config.idleTimeoutMillis ?? 30000
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// CONNECTION MANAGEMENT
|
|
73
|
+
// ============================================================================
|
|
74
|
+
async connect() {
|
|
75
|
+
if (this.pool) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.pool = await sql.connect(this.sqlConfig);
|
|
79
|
+
}
|
|
80
|
+
async test() {
|
|
81
|
+
try {
|
|
82
|
+
await this.connect();
|
|
83
|
+
const result = await this.executeQuery(`
|
|
84
|
+
SELECT
|
|
85
|
+
1 as test,
|
|
86
|
+
@@VERSION as version,
|
|
87
|
+
DB_NAME() as db
|
|
88
|
+
`);
|
|
89
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
message: `Successfully connected to ${result.data[0].db} on ${this.config.host}`,
|
|
93
|
+
serverVersion: result.data[0].version,
|
|
94
|
+
databaseName: result.data[0].db
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
message: 'Connection established but test query failed'
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
message: `Connection failed: ${error.message}`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async close() {
|
|
110
|
+
if (this.pool) {
|
|
111
|
+
await this.pool.close();
|
|
112
|
+
this.pool = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// SCHEMA INTROSPECTION
|
|
117
|
+
// ============================================================================
|
|
118
|
+
async getSchemas(schemaFilter, tableFilter) {
|
|
119
|
+
// Get all tables grouped by schema
|
|
120
|
+
const tablesQuery = this.buildTablesQuery(schemaFilter, tableFilter);
|
|
121
|
+
const tablesResult = await this.executeQuery(tablesQuery);
|
|
122
|
+
if (!tablesResult.success || !tablesResult.data) {
|
|
123
|
+
throw new Error(`Failed to get tables: ${tablesResult.errorMessage}`);
|
|
124
|
+
}
|
|
125
|
+
// Group tables by schema
|
|
126
|
+
const schemaMap = new Map();
|
|
127
|
+
for (const row of tablesResult.data) {
|
|
128
|
+
if (!schemaMap.has(row.schema_name)) {
|
|
129
|
+
schemaMap.set(row.schema_name, []);
|
|
130
|
+
}
|
|
131
|
+
schemaMap.get(row.schema_name).push({
|
|
132
|
+
tableName: row.table_name,
|
|
133
|
+
rowCount: row.row_count
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// Build schemas with full table details
|
|
137
|
+
const schemas = [];
|
|
138
|
+
for (const [schemaName, tableSummaries] of schemaMap) {
|
|
139
|
+
const tables = [];
|
|
140
|
+
for (const { tableName, rowCount } of tableSummaries) {
|
|
141
|
+
const [columns, foreignKeys, primaryKeys] = await Promise.all([
|
|
142
|
+
this.getColumns(schemaName, tableName),
|
|
143
|
+
this.getForeignKeys(schemaName, tableName),
|
|
144
|
+
this.getPrimaryKeys(schemaName, tableName)
|
|
145
|
+
]);
|
|
146
|
+
tables.push({
|
|
147
|
+
schemaName,
|
|
148
|
+
tableName,
|
|
149
|
+
rowCount,
|
|
150
|
+
columns,
|
|
151
|
+
foreignKeys,
|
|
152
|
+
primaryKeys
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
schemas.push({
|
|
156
|
+
name: schemaName,
|
|
157
|
+
tables
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return schemas;
|
|
161
|
+
}
|
|
162
|
+
async getTables(schemaName, tableFilter) {
|
|
163
|
+
const query = this.buildTablesQuery({ include: [schemaName] }, tableFilter);
|
|
164
|
+
const result = await this.executeQuery(query);
|
|
165
|
+
if (!result.success || !result.data) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const tables = [];
|
|
169
|
+
for (const row of result.data) {
|
|
170
|
+
const [columns, foreignKeys, primaryKeys] = await Promise.all([
|
|
171
|
+
this.getColumns(row.schema_name, row.table_name),
|
|
172
|
+
this.getForeignKeys(row.schema_name, row.table_name),
|
|
173
|
+
this.getPrimaryKeys(row.schema_name, row.table_name)
|
|
174
|
+
]);
|
|
175
|
+
tables.push({
|
|
176
|
+
schemaName: row.schema_name,
|
|
177
|
+
tableName: row.table_name,
|
|
178
|
+
rowCount: row.row_count,
|
|
179
|
+
columns,
|
|
180
|
+
foreignKeys,
|
|
181
|
+
primaryKeys
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return tables;
|
|
185
|
+
}
|
|
186
|
+
async getColumns(schemaName, tableName) {
|
|
187
|
+
const query = `
|
|
188
|
+
SELECT
|
|
189
|
+
c.name as column_name,
|
|
190
|
+
t.name as data_type,
|
|
191
|
+
c.is_nullable,
|
|
192
|
+
c.max_length,
|
|
193
|
+
c.precision,
|
|
194
|
+
c.scale,
|
|
195
|
+
CAST(CASE WHEN pk.column_id IS NOT NULL THEN 1 ELSE 0 END AS BIT) as is_primary_key,
|
|
196
|
+
CAST(CASE WHEN fk.parent_column_id IS NOT NULL THEN 1 ELSE 0 END AS BIT) as is_foreign_key,
|
|
197
|
+
cc.definition as check_constraint,
|
|
198
|
+
dc.definition as default_value
|
|
199
|
+
FROM sys.columns c
|
|
200
|
+
INNER JOIN sys.types t ON c.user_type_id = t.user_type_id
|
|
201
|
+
INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
|
|
202
|
+
INNER JOIN sys.schemas s ON tbl.schema_id = s.schema_id
|
|
203
|
+
LEFT JOIN (
|
|
204
|
+
SELECT ic.object_id, ic.column_id
|
|
205
|
+
FROM sys.index_columns ic
|
|
206
|
+
INNER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
|
207
|
+
WHERE i.is_primary_key = 1
|
|
208
|
+
) pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
|
|
209
|
+
LEFT JOIN sys.foreign_key_columns fk ON c.object_id = fk.parent_object_id AND c.column_id = fk.parent_column_id
|
|
210
|
+
LEFT JOIN sys.check_constraints cc ON c.object_id = cc.parent_object_id AND cc.parent_column_id = c.column_id
|
|
211
|
+
LEFT JOIN sys.default_constraints dc ON c.object_id = dc.parent_object_id AND c.column_id = dc.parent_column_id
|
|
212
|
+
WHERE s.name = '${schemaName}' AND tbl.name = '${tableName}'
|
|
213
|
+
ORDER BY c.column_id
|
|
214
|
+
`;
|
|
215
|
+
const result = await this.executeQuery(query);
|
|
216
|
+
if (!result.success || !result.data) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
return result.data.map(row => ({
|
|
220
|
+
name: row.column_name,
|
|
221
|
+
dataType: row.data_type,
|
|
222
|
+
isNullable: row.is_nullable,
|
|
223
|
+
isPrimaryKey: row.is_primary_key,
|
|
224
|
+
isForeignKey: row.is_foreign_key,
|
|
225
|
+
checkConstraint: row.check_constraint || undefined,
|
|
226
|
+
defaultValue: row.default_value || undefined,
|
|
227
|
+
maxLength: row.max_length > 0 ? row.max_length : undefined,
|
|
228
|
+
precision: row.precision > 0 ? row.precision : undefined,
|
|
229
|
+
scale: row.scale > 0 ? row.scale : undefined
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
async getExistingDescriptions(schemaName, tableName) {
|
|
233
|
+
const query = `
|
|
234
|
+
SELECT
|
|
235
|
+
ISNULL(c.name, '') as column_name,
|
|
236
|
+
CAST(ep.value AS NVARCHAR(MAX)) as description
|
|
237
|
+
FROM sys.extended_properties ep
|
|
238
|
+
INNER JOIN sys.tables t ON ep.major_id = t.object_id
|
|
239
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
240
|
+
LEFT JOIN sys.columns c ON ep.major_id = c.object_id AND ep.minor_id = c.column_id
|
|
241
|
+
WHERE ep.name = 'MS_Description'
|
|
242
|
+
AND s.name = '${schemaName}'
|
|
243
|
+
AND t.name = '${tableName}'
|
|
244
|
+
`;
|
|
245
|
+
const result = await this.executeQuery(query);
|
|
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 ? result.data[0].distinct_count : 0;
|
|
302
|
+
}
|
|
303
|
+
async getValueDistribution(schemaName, tableName, columnName, limit) {
|
|
304
|
+
const query = `
|
|
305
|
+
SELECT TOP ${limit}
|
|
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
|
+
`;
|
|
313
|
+
const result = await this.executeQuery(query);
|
|
314
|
+
return result.success && result.data ? result.data : [];
|
|
315
|
+
}
|
|
316
|
+
async getSampleValues(schemaName, tableName, columnName, sampleSize) {
|
|
317
|
+
// **IMPORTANT**: Limit sample size to max 20 to reduce JSON size
|
|
318
|
+
// User requested: "narrow that down to maybe 10-20 values randomly selected from each col"
|
|
319
|
+
const limitedSampleSize = Math.min(sampleSize, 20);
|
|
320
|
+
const query = `
|
|
321
|
+
SELECT TOP ${limitedSampleSize} ${this.escapeIdentifier(columnName)} as value
|
|
322
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
323
|
+
WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
|
|
324
|
+
ORDER BY NEWID()
|
|
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) {
|
|
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 result = await this.pool.request().query(query);
|
|
340
|
+
return {
|
|
341
|
+
success: true,
|
|
342
|
+
data: result.recordset,
|
|
343
|
+
rowCount: result.rowsAffected[0]
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
lastError = error;
|
|
348
|
+
// Check if error is transient
|
|
349
|
+
if (this.isTransientError(error) && attempt < maxRetries) {
|
|
350
|
+
await this.sleep(Math.pow(2, attempt) * 1000);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
success: false,
|
|
358
|
+
errorMessage: lastError?.message || 'Unknown error'
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// PROVIDER-SPECIFIC HELPERS
|
|
363
|
+
// ============================================================================
|
|
364
|
+
escapeIdentifier(identifier) {
|
|
365
|
+
return `[${identifier}]`;
|
|
366
|
+
}
|
|
367
|
+
getLimitClause(limit) {
|
|
368
|
+
return `TOP ${limit}`;
|
|
369
|
+
}
|
|
370
|
+
// ============================================================================
|
|
371
|
+
// PRIVATE HELPER METHODS
|
|
372
|
+
// ============================================================================
|
|
373
|
+
buildTablesQuery(schemaFilter, tableFilter) {
|
|
374
|
+
let whereClause = 'WHERE t.is_ms_shipped = 0';
|
|
375
|
+
whereClause += this.buildSchemaFilterClause(schemaFilter, 's.name');
|
|
376
|
+
whereClause += this.buildTableFilterClause(tableFilter, 't.name');
|
|
377
|
+
return `
|
|
378
|
+
SELECT
|
|
379
|
+
s.name as schema_name,
|
|
380
|
+
t.name as table_name,
|
|
381
|
+
ISNULL(p.rows, 0) as row_count
|
|
382
|
+
FROM sys.tables t
|
|
383
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
384
|
+
LEFT JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0, 1)
|
|
385
|
+
${whereClause}
|
|
386
|
+
ORDER BY s.name, t.name
|
|
387
|
+
`;
|
|
388
|
+
}
|
|
389
|
+
async getForeignKeys(schemaName, tableName) {
|
|
390
|
+
const query = `
|
|
391
|
+
SELECT
|
|
392
|
+
c.name as column_name,
|
|
393
|
+
rs.name as referenced_schema,
|
|
394
|
+
rt.name as referenced_table,
|
|
395
|
+
rc.name as referenced_column,
|
|
396
|
+
fkc.name as constraint_name
|
|
397
|
+
FROM sys.foreign_key_columns fk
|
|
398
|
+
INNER JOIN sys.foreign_keys fkc ON fk.constraint_object_id = fkc.object_id
|
|
399
|
+
INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id
|
|
400
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
401
|
+
INNER JOIN sys.columns c ON fk.parent_object_id = c.object_id AND fk.parent_column_id = c.column_id
|
|
402
|
+
INNER JOIN sys.tables rt ON fk.referenced_object_id = rt.object_id
|
|
403
|
+
INNER JOIN sys.schemas rs ON rt.schema_id = rs.schema_id
|
|
404
|
+
INNER JOIN sys.columns rc ON fk.referenced_object_id = rc.object_id AND fk.referenced_column_id = rc.column_id
|
|
405
|
+
WHERE s.name = '${schemaName}' AND t.name = '${tableName}'
|
|
406
|
+
`;
|
|
407
|
+
const result = await this.executeQuery(query);
|
|
408
|
+
if (!result.success || !result.data) {
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
return result.data.map(row => ({
|
|
412
|
+
columnName: row.column_name,
|
|
413
|
+
referencedSchema: row.referenced_schema,
|
|
414
|
+
referencedTable: row.referenced_table,
|
|
415
|
+
referencedColumn: row.referenced_column,
|
|
416
|
+
constraintName: row.constraint_name
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
async getPrimaryKeys(schemaName, tableName) {
|
|
420
|
+
const query = `
|
|
421
|
+
SELECT
|
|
422
|
+
c.name as column_name,
|
|
423
|
+
ic.key_ordinal as ordinal_position,
|
|
424
|
+
i.name as constraint_name
|
|
425
|
+
FROM sys.index_columns ic
|
|
426
|
+
INNER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
|
427
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
428
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
429
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
430
|
+
WHERE i.is_primary_key = 1
|
|
431
|
+
AND s.name = '${schemaName}'
|
|
432
|
+
AND t.name = '${tableName}'
|
|
433
|
+
ORDER BY ic.key_ordinal
|
|
434
|
+
`;
|
|
435
|
+
const result = await this.executeQuery(query);
|
|
436
|
+
if (!result.success || !result.data) {
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
return result.data.map(row => ({
|
|
440
|
+
columnName: row.column_name,
|
|
441
|
+
ordinalPosition: row.ordinal_position,
|
|
442
|
+
constraintName: row.constraint_name
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
async getCardinalityStats(schemaName, tableName, columnName) {
|
|
446
|
+
const query = `
|
|
447
|
+
SELECT
|
|
448
|
+
COUNT(DISTINCT ${this.escapeIdentifier(columnName)}) as distinct_count,
|
|
449
|
+
COUNT(*) as total_count,
|
|
450
|
+
SUM(CASE WHEN ${this.escapeIdentifier(columnName)} IS NULL THEN 1 ELSE 0 END) as null_count
|
|
451
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
452
|
+
`;
|
|
453
|
+
const result = await this.executeQuery(query);
|
|
454
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
455
|
+
const row = result.data[0];
|
|
456
|
+
const totalCount = row.total_count || 1;
|
|
457
|
+
return {
|
|
458
|
+
distinctCount: row.distinct_count,
|
|
459
|
+
uniquenessRatio: row.distinct_count / totalCount,
|
|
460
|
+
nullCount: row.null_count,
|
|
461
|
+
nullPercentage: (row.null_count / totalCount) * 100,
|
|
462
|
+
totalCount
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
distinctCount: 0,
|
|
467
|
+
uniquenessRatio: 0,
|
|
468
|
+
nullCount: 0,
|
|
469
|
+
nullPercentage: 0,
|
|
470
|
+
totalCount: 0
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
async getNumericStats(schemaName, tableName, columnName) {
|
|
474
|
+
const query = `
|
|
475
|
+
SELECT
|
|
476
|
+
MIN(${this.escapeIdentifier(columnName)}) as min_value,
|
|
477
|
+
MAX(${this.escapeIdentifier(columnName)}) as max_value,
|
|
478
|
+
AVG(CAST(${this.escapeIdentifier(columnName)} AS FLOAT)) as avg_value,
|
|
479
|
+
STDEV(${this.escapeIdentifier(columnName)}) as std_dev
|
|
480
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
481
|
+
WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
|
|
482
|
+
`;
|
|
483
|
+
const result = await this.executeQuery(query);
|
|
484
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
485
|
+
const row = result.data[0];
|
|
486
|
+
return {
|
|
487
|
+
min: row.min_value,
|
|
488
|
+
max: row.max_value,
|
|
489
|
+
avg: row.avg_value,
|
|
490
|
+
stdDev: row.std_dev
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return {};
|
|
494
|
+
}
|
|
495
|
+
async getDateStats(schemaName, tableName, columnName) {
|
|
496
|
+
const query = `
|
|
497
|
+
SELECT
|
|
498
|
+
MIN(${this.escapeIdentifier(columnName)}) as min_value,
|
|
499
|
+
MAX(${this.escapeIdentifier(columnName)}) as max_value
|
|
500
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
501
|
+
WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
|
|
502
|
+
`;
|
|
503
|
+
const result = await this.executeQuery(query);
|
|
504
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
505
|
+
const row = result.data[0];
|
|
506
|
+
return {
|
|
507
|
+
min: row.min_value,
|
|
508
|
+
max: row.max_value
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
return {};
|
|
512
|
+
}
|
|
513
|
+
async getStringStats(schemaName, tableName, columnName) {
|
|
514
|
+
const query = `
|
|
515
|
+
SELECT
|
|
516
|
+
AVG(LEN(${this.escapeIdentifier(columnName)})) as avg_length,
|
|
517
|
+
MAX(LEN(${this.escapeIdentifier(columnName)})) as max_length,
|
|
518
|
+
MIN(LEN(${this.escapeIdentifier(columnName)})) as min_length
|
|
519
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
520
|
+
WHERE ${this.escapeIdentifier(columnName)} IS NOT NULL
|
|
521
|
+
`;
|
|
522
|
+
const result = await this.executeQuery(query);
|
|
523
|
+
if (result.success && result.data && result.data.length > 0) {
|
|
524
|
+
const row = result.data[0];
|
|
525
|
+
return {
|
|
526
|
+
avgLength: row.avg_length,
|
|
527
|
+
maxLength: row.max_length,
|
|
528
|
+
minLength: row.min_length
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
return {};
|
|
532
|
+
}
|
|
533
|
+
isTransientError(error) {
|
|
534
|
+
const transientMessages = [
|
|
535
|
+
'connection',
|
|
536
|
+
'timeout',
|
|
537
|
+
'deadlock',
|
|
538
|
+
'network',
|
|
539
|
+
'transport'
|
|
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
|
+
c.name,
|
|
554
|
+
t.name as type,
|
|
555
|
+
c.is_nullable as nullable
|
|
556
|
+
FROM sys.columns c
|
|
557
|
+
INNER JOIN sys.types t ON c.user_type_id = t.user_type_id
|
|
558
|
+
INNER JOIN sys.tables tbl ON c.object_id = tbl.object_id
|
|
559
|
+
INNER JOIN sys.schemas s ON tbl.schema_id = s.schema_id
|
|
560
|
+
WHERE s.name = '${schemaName}'
|
|
561
|
+
AND tbl.name = '${tableName}'
|
|
562
|
+
AND c.name = '${columnName}'
|
|
563
|
+
`;
|
|
564
|
+
const result = await this.executeQuery(query);
|
|
565
|
+
if (!result.success || !result.data || result.data.length === 0) {
|
|
566
|
+
throw new Error(`Column ${schemaName}.${tableName}.${columnName} not found`);
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
name: result.data[0].name,
|
|
570
|
+
type: result.data[0].type,
|
|
571
|
+
nullable: result.data[0].nullable
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Test value overlap between source and target columns
|
|
576
|
+
*/
|
|
577
|
+
async testValueOverlap(sourceTable, sourceColumn, targetTable, targetColumn, sampleSize) {
|
|
578
|
+
try {
|
|
579
|
+
const [sourceSchema, sourceTableName] = this.parseTableIdentifier(sourceTable);
|
|
580
|
+
const [targetSchema, targetTableName] = this.parseTableIdentifier(targetTable);
|
|
581
|
+
const query = `
|
|
582
|
+
WITH SourceSample AS (
|
|
583
|
+
SELECT DISTINCT TOP ${sampleSize}
|
|
584
|
+
${this.escapeIdentifier(sourceColumn)} as value
|
|
585
|
+
FROM ${this.escapeIdentifier(sourceSchema)}.${this.escapeIdentifier(sourceTableName)}
|
|
586
|
+
WHERE ${this.escapeIdentifier(sourceColumn)} IS NOT NULL
|
|
587
|
+
),
|
|
588
|
+
TargetValues AS (
|
|
589
|
+
SELECT DISTINCT ${this.escapeIdentifier(targetColumn)} as value
|
|
590
|
+
FROM ${this.escapeIdentifier(targetSchema)}.${this.escapeIdentifier(targetTableName)}
|
|
591
|
+
WHERE ${this.escapeIdentifier(targetColumn)} IS NOT NULL
|
|
592
|
+
)
|
|
593
|
+
SELECT
|
|
594
|
+
COUNT(*) as total_source,
|
|
595
|
+
SUM(CASE WHEN tv.value IS NOT NULL THEN 1 ELSE 0 END) as matching_count
|
|
596
|
+
FROM SourceSample ss
|
|
597
|
+
LEFT JOIN TargetValues tv ON ss.value = tv.value
|
|
598
|
+
`;
|
|
599
|
+
const result = await this.executeQuery(query);
|
|
600
|
+
if (!result.success || !result.data || result.data.length === 0) {
|
|
601
|
+
return 0;
|
|
602
|
+
}
|
|
603
|
+
const row = result.data[0];
|
|
604
|
+
if (row.total_source === 0) {
|
|
605
|
+
return 0;
|
|
606
|
+
}
|
|
607
|
+
return row.matching_count / row.total_source;
|
|
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 TOP ${sampleSize}
|
|
626
|
+
${columnList}
|
|
627
|
+
FROM ${this.escapeIdentifier(schemaName)}.${this.escapeIdentifier(tableName)}
|
|
628
|
+
WHERE ${escapedColumns.map(col => `${col} IS NOT NULL`).join(' AND ')}
|
|
629
|
+
),
|
|
630
|
+
GroupedData AS (
|
|
631
|
+
SELECT
|
|
632
|
+
${columnList},
|
|
633
|
+
COUNT(*) as occurrence_count
|
|
634
|
+
FROM SampledData
|
|
635
|
+
GROUP BY ${columnList}
|
|
636
|
+
HAVING COUNT(*) > 1
|
|
637
|
+
)
|
|
638
|
+
SELECT COUNT(*) as duplicate_count
|
|
639
|
+
FROM GroupedData
|
|
640
|
+
`;
|
|
641
|
+
const result = await this.executeQuery(query);
|
|
642
|
+
if (!result.success || !result.data || result.data.length === 0) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
return result.data[0].duplicate_count === 0;
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Parse table identifier in format "schema.table"
|
|
653
|
+
*/
|
|
654
|
+
parseTableIdentifier(tableIdentifier) {
|
|
655
|
+
const parts = tableIdentifier.split('.');
|
|
656
|
+
if (parts.length !== 2) {
|
|
657
|
+
throw new Error(`Invalid table identifier format: ${tableIdentifier}. Expected "schema.table"`);
|
|
658
|
+
}
|
|
659
|
+
return [parts[0], parts[1]];
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
exports.SQLServerDriver = SQLServerDriver;
|
|
663
|
+
exports.SQLServerDriver = SQLServerDriver = __decorate([
|
|
664
|
+
(0, global_1.RegisterClass)(BaseAutoDocDriver_js_1.BaseAutoDocDriver, 'SQLServer'),
|
|
665
|
+
__metadata("design:paramtypes", [Object])
|
|
666
|
+
], SQLServerDriver);
|
|
667
|
+
//# sourceMappingURL=SQLServerDriver.js.map
|