dbdock 1.1.15 → 1.1.17
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 +473 -580
- package/dist/cli/commands/analyze.d.ts +1 -0
- package/dist/cli/commands/analyze.js +135 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/cross-migrate.d.ts +11 -0
- package/dist/cli/commands/cross-migrate.js +307 -0
- package/dist/cli/commands/cross-migrate.js.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/migration/analyzers/mongodb.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/mongodb.analyzer.js +208 -0
- package/dist/migration/analyzers/mongodb.analyzer.js.map +1 -0
- package/dist/migration/analyzers/postgres.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/postgres.analyzer.js +176 -0
- package/dist/migration/analyzers/postgres.analyzer.js.map +1 -0
- package/dist/migration/config.manager.d.ts +3 -0
- package/dist/migration/config.manager.js +80 -0
- package/dist/migration/config.manager.js.map +1 -0
- package/dist/migration/engines/migration.engine.d.ts +6 -0
- package/dist/migration/engines/migration.engine.js +62 -0
- package/dist/migration/engines/migration.engine.js.map +1 -0
- package/dist/migration/engines/mongo-to-postgres.engine.d.ts +2 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js +409 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js.map +1 -0
- package/dist/migration/engines/postgres-to-mongo.engine.d.ts +2 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js +192 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js.map +1 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.d.ts +2 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js +263 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js.map +1 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.d.ts +2 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js +174 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js.map +1 -0
- package/dist/migration/reference.detector.d.ts +2 -0
- package/dist/migration/reference.detector.js +57 -0
- package/dist/migration/reference.detector.js.map +1 -0
- package/dist/migration/type.mapper.d.ts +12 -0
- package/dist/migration/type.mapper.js +197 -0
- package/dist/migration/type.mapper.js.map +1 -0
- package/dist/migration/types.d.ts +183 -0
- package/dist/migration/types.js +11 -0
- package/dist/migration/types.js.map +1 -0
- package/package.json +11 -2
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMongoUrl = parseMongoUrl;
|
|
4
|
+
exports.analyzeMongoDB = analyzeMongoDB;
|
|
5
|
+
const mongodb_1 = require("mongodb");
|
|
6
|
+
const type_mapper_1 = require("../type.mapper");
|
|
7
|
+
const SAMPLE_SIZE = 5000;
|
|
8
|
+
const MAX_SAMPLE_VALUES = 5;
|
|
9
|
+
function parseMongoUrl(urlString) {
|
|
10
|
+
const url = new URL(urlString);
|
|
11
|
+
if (url.protocol !== 'mongodb:' && url.protocol !== 'mongodb+srv:') {
|
|
12
|
+
throw new Error(`Invalid protocol "${url.protocol}". Expected "mongodb://" or "mongodb+srv://"`);
|
|
13
|
+
}
|
|
14
|
+
const database = url.pathname.replace(/^\//, '') || 'test';
|
|
15
|
+
return {
|
|
16
|
+
type: 'mongodb',
|
|
17
|
+
url: urlString,
|
|
18
|
+
database,
|
|
19
|
+
host: url.hostname || 'localhost',
|
|
20
|
+
port: parseInt(url.port || '27017'),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async function analyzeMongoDB(connectionUrl, sampleSize = SAMPLE_SIZE) {
|
|
24
|
+
const parsed = parseMongoUrl(connectionUrl);
|
|
25
|
+
const client = new mongodb_1.MongoClient(connectionUrl);
|
|
26
|
+
try {
|
|
27
|
+
await client.connect();
|
|
28
|
+
const db = client.db(parsed.database);
|
|
29
|
+
const collectionNames = await getCollectionNames(db);
|
|
30
|
+
const collections = [];
|
|
31
|
+
let totalDocuments = 0;
|
|
32
|
+
for (const name of collectionNames) {
|
|
33
|
+
const analysis = await analyzeCollection(db, name, sampleSize);
|
|
34
|
+
collections.push(analysis);
|
|
35
|
+
totalDocuments += analysis.documentCount;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
database: parsed.database,
|
|
39
|
+
type: 'mongodb',
|
|
40
|
+
collections,
|
|
41
|
+
totalDocuments,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await client.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function getCollectionNames(db) {
|
|
49
|
+
const collections = await db.listCollections().toArray();
|
|
50
|
+
return collections
|
|
51
|
+
.filter((c) => c.type === 'collection')
|
|
52
|
+
.map((c) => c.name)
|
|
53
|
+
.filter((n) => !n.startsWith('system.'));
|
|
54
|
+
}
|
|
55
|
+
async function analyzeCollection(db, collectionName, sampleSize) {
|
|
56
|
+
const collection = db.collection(collectionName);
|
|
57
|
+
const documentCount = await collection.countDocuments();
|
|
58
|
+
let documents;
|
|
59
|
+
if (documentCount <= sampleSize) {
|
|
60
|
+
documents = await collection.find({}).toArray();
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
documents = await collection.aggregate([{ $sample: { size: sampleSize } }]).toArray();
|
|
64
|
+
}
|
|
65
|
+
const fieldMap = new Map();
|
|
66
|
+
const sampledCount = documents.length;
|
|
67
|
+
for (const doc of documents) {
|
|
68
|
+
analyzeDocument(doc, '', fieldMap, 0);
|
|
69
|
+
}
|
|
70
|
+
const fields = buildFieldInfos(fieldMap, sampledCount, documentCount);
|
|
71
|
+
const indexes = await collection.indexes().catch(() => []);
|
|
72
|
+
const indexInfo = indexes.map((idx) => ({
|
|
73
|
+
name: idx.name,
|
|
74
|
+
key: idx.key,
|
|
75
|
+
unique: idx.unique || false,
|
|
76
|
+
}));
|
|
77
|
+
return {
|
|
78
|
+
name: collectionName,
|
|
79
|
+
documentCount,
|
|
80
|
+
fields,
|
|
81
|
+
indexes: indexInfo,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function getOrCreateAccumulator(map, path, name, depth) {
|
|
85
|
+
if (!map.has(path)) {
|
|
86
|
+
map.set(path, {
|
|
87
|
+
name,
|
|
88
|
+
path,
|
|
89
|
+
typeCounts: {},
|
|
90
|
+
count: 0,
|
|
91
|
+
isArray: false,
|
|
92
|
+
isObjectId: false,
|
|
93
|
+
isNestedObject: false,
|
|
94
|
+
sampleValues: [],
|
|
95
|
+
depth,
|
|
96
|
+
children: new Map(),
|
|
97
|
+
arrayElementTypes: new Set(),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return map.get(path);
|
|
101
|
+
}
|
|
102
|
+
function analyzeDocument(doc, prefix, fieldMap, depth) {
|
|
103
|
+
if (!doc || typeof doc !== 'object' || Array.isArray(doc))
|
|
104
|
+
return;
|
|
105
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
106
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
107
|
+
const acc = getOrCreateAccumulator(fieldMap, path, key, depth);
|
|
108
|
+
acc.count++;
|
|
109
|
+
const fieldType = (0, type_mapper_1.detectMongoFieldType)(value);
|
|
110
|
+
acc.typeCounts[fieldType] = (acc.typeCounts[fieldType] || 0) + 1;
|
|
111
|
+
if (acc.sampleValues.length < MAX_SAMPLE_VALUES) {
|
|
112
|
+
acc.sampleValues.push(summarizeValue(value));
|
|
113
|
+
}
|
|
114
|
+
if (fieldType === 'objectId') {
|
|
115
|
+
acc.isObjectId = true;
|
|
116
|
+
}
|
|
117
|
+
if (fieldType === 'array' && Array.isArray(value)) {
|
|
118
|
+
acc.isArray = true;
|
|
119
|
+
for (const element of value.slice(0, 100)) {
|
|
120
|
+
const elType = (0, type_mapper_1.detectMongoFieldType)(element);
|
|
121
|
+
acc.arrayElementTypes.add(elType);
|
|
122
|
+
if (elType === 'object' && element && typeof element === 'object') {
|
|
123
|
+
analyzeDocument(element, path + '[]', fieldMap, depth + 1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (fieldType === 'object' && value && !isSpecialBsonType(value)) {
|
|
128
|
+
acc.isNestedObject = true;
|
|
129
|
+
analyzeDocument(value, path, fieldMap, depth + 1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function isSpecialBsonType(value) {
|
|
134
|
+
if (!value || typeof value !== 'object')
|
|
135
|
+
return false;
|
|
136
|
+
return !!(value._bsontype || value instanceof mongodb_1.ObjectId || value instanceof Date);
|
|
137
|
+
}
|
|
138
|
+
function summarizeValue(value) {
|
|
139
|
+
if (value === null || value === undefined)
|
|
140
|
+
return value;
|
|
141
|
+
if (typeof value === 'string')
|
|
142
|
+
return value.length > 50 ? value.slice(0, 50) + '...' : value;
|
|
143
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
144
|
+
return value;
|
|
145
|
+
if (value instanceof Date)
|
|
146
|
+
return value.toISOString();
|
|
147
|
+
if (value instanceof mongodb_1.ObjectId || value?._bsontype === 'ObjectId')
|
|
148
|
+
return value.toString();
|
|
149
|
+
if (Array.isArray(value))
|
|
150
|
+
return `[Array(${value.length})]`;
|
|
151
|
+
if (typeof value === 'object')
|
|
152
|
+
return `{Object(${Object.keys(value).length} keys)}`;
|
|
153
|
+
return String(value);
|
|
154
|
+
}
|
|
155
|
+
function buildFieldInfos(fieldMap, sampledCount, totalCount) {
|
|
156
|
+
const topLevelFields = [];
|
|
157
|
+
const topLevelEntries = Array.from(fieldMap.entries()).filter(([path]) => !path.includes('.'));
|
|
158
|
+
for (const [, acc] of topLevelEntries) {
|
|
159
|
+
const field = accumulatorToFieldInfo(acc, fieldMap, sampledCount, totalCount);
|
|
160
|
+
topLevelFields.push(field);
|
|
161
|
+
}
|
|
162
|
+
topLevelFields.sort((a, b) => b.frequency - a.frequency);
|
|
163
|
+
return topLevelFields;
|
|
164
|
+
}
|
|
165
|
+
function accumulatorToFieldInfo(acc, fieldMap, sampledCount, totalCount) {
|
|
166
|
+
const frequency = sampledCount > 0
|
|
167
|
+
? Math.round((acc.count / sampledCount) * 100 * 100) / 100
|
|
168
|
+
: 0;
|
|
169
|
+
const nestedFields = [];
|
|
170
|
+
const childPrefix = acc.path + '.';
|
|
171
|
+
const arrayChildPrefix = acc.path + '[].';
|
|
172
|
+
for (const [path, childAcc] of fieldMap.entries()) {
|
|
173
|
+
if (path.startsWith(childPrefix) || path.startsWith(arrayChildPrefix)) {
|
|
174
|
+
const remaining = path.startsWith(childPrefix)
|
|
175
|
+
? path.slice(childPrefix.length)
|
|
176
|
+
: path.slice(arrayChildPrefix.length);
|
|
177
|
+
if (!remaining.includes('.') && !remaining.includes('[]')) {
|
|
178
|
+
nestedFields.push(accumulatorToFieldInfo(childAcc, fieldMap, sampledCount, totalCount));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
nestedFields.sort((a, b) => b.frequency - a.frequency);
|
|
183
|
+
const possibleReference = acc.isObjectId && acc.name !== '_id'
|
|
184
|
+
? guessReferenceFromName(acc.name)
|
|
185
|
+
: undefined;
|
|
186
|
+
return {
|
|
187
|
+
name: acc.name,
|
|
188
|
+
path: acc.path,
|
|
189
|
+
types: acc.typeCounts,
|
|
190
|
+
totalCount: Math.round((acc.count / sampledCount) * totalCount),
|
|
191
|
+
frequency,
|
|
192
|
+
isArray: acc.isArray,
|
|
193
|
+
isObjectId: acc.isObjectId,
|
|
194
|
+
isNestedObject: acc.isNestedObject,
|
|
195
|
+
nestedFields: nestedFields.length > 0 ? nestedFields : undefined,
|
|
196
|
+
arrayElementType: acc.arrayElementTypes.size > 0
|
|
197
|
+
? Array.from(acc.arrayElementTypes).join(' | ')
|
|
198
|
+
: undefined,
|
|
199
|
+
possibleReference,
|
|
200
|
+
sampleValues: acc.sampleValues,
|
|
201
|
+
depth: acc.depth,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function guessReferenceFromName(fieldName) {
|
|
205
|
+
const cleaned = fieldName.replace(/(_id|Id)$/, '');
|
|
206
|
+
return cleaned || undefined;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=mongodb.analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodb.analyzer.js","sourceRoot":"","sources":["../../../src/migration/analyzers/mongodb.analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parsePostgresUrl = parsePostgresUrl;
|
|
4
|
+
exports.analyzePostgres = analyzePostgres;
|
|
5
|
+
const pg_1 = require("pg");
|
|
6
|
+
function parsePostgresUrl(urlString) {
|
|
7
|
+
const url = new URL(urlString);
|
|
8
|
+
if (url.protocol !== 'postgresql:' && url.protocol !== 'postgres:') {
|
|
9
|
+
throw new Error(`Invalid protocol "${url.protocol}". Expected "postgresql://" or "postgres://"`);
|
|
10
|
+
}
|
|
11
|
+
const database = url.pathname.replace(/^\//, '') || 'postgres';
|
|
12
|
+
return {
|
|
13
|
+
type: 'postgresql',
|
|
14
|
+
url: urlString,
|
|
15
|
+
database,
|
|
16
|
+
host: url.hostname || 'localhost',
|
|
17
|
+
port: parseInt(url.port || '5432'),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async function analyzePostgres(connectionUrl) {
|
|
21
|
+
const parsed = parsePostgresUrl(connectionUrl);
|
|
22
|
+
const pool = new pg_1.Pool({ connectionString: connectionUrl });
|
|
23
|
+
try {
|
|
24
|
+
const tableNames = await getTableNames(pool);
|
|
25
|
+
const tables = [];
|
|
26
|
+
let totalRows = 0;
|
|
27
|
+
for (const tableName of tableNames) {
|
|
28
|
+
const analysis = await analyzeTable(pool, tableName);
|
|
29
|
+
tables.push(analysis);
|
|
30
|
+
totalRows += analysis.rowCount;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
database: parsed.database,
|
|
34
|
+
type: 'postgresql',
|
|
35
|
+
tables,
|
|
36
|
+
totalRows,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await pool.end();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function getTableNames(pool) {
|
|
44
|
+
const result = await pool.query(`
|
|
45
|
+
SELECT table_name
|
|
46
|
+
FROM information_schema.tables
|
|
47
|
+
WHERE table_schema = 'public'
|
|
48
|
+
AND table_type = 'BASE TABLE'
|
|
49
|
+
ORDER BY table_name
|
|
50
|
+
`);
|
|
51
|
+
return result.rows.map((r) => r.table_name);
|
|
52
|
+
}
|
|
53
|
+
async function analyzeTable(pool, tableName) {
|
|
54
|
+
const [columns, foreignKeys, indexes, rowCount] = await Promise.all([
|
|
55
|
+
getColumns(pool, tableName),
|
|
56
|
+
getForeignKeys(pool, tableName),
|
|
57
|
+
getIndexes(pool, tableName),
|
|
58
|
+
getRowCount(pool, tableName),
|
|
59
|
+
]);
|
|
60
|
+
return {
|
|
61
|
+
name: tableName,
|
|
62
|
+
schema: 'public',
|
|
63
|
+
columns,
|
|
64
|
+
foreignKeys,
|
|
65
|
+
indexes,
|
|
66
|
+
rowCount,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function getColumns(pool, tableName) {
|
|
70
|
+
const result = await pool.query(`
|
|
71
|
+
SELECT
|
|
72
|
+
c.column_name,
|
|
73
|
+
c.data_type,
|
|
74
|
+
c.udt_name,
|
|
75
|
+
c.is_nullable,
|
|
76
|
+
c.column_default,
|
|
77
|
+
c.character_maximum_length,
|
|
78
|
+
c.numeric_precision,
|
|
79
|
+
COALESCE(pk.is_pk, false) AS is_primary_key,
|
|
80
|
+
COALESCE(uq.is_unique, false) AS is_unique
|
|
81
|
+
FROM information_schema.columns c
|
|
82
|
+
LEFT JOIN (
|
|
83
|
+
SELECT kcu.column_name, true AS is_pk
|
|
84
|
+
FROM information_schema.table_constraints tc
|
|
85
|
+
JOIN information_schema.key_column_usage kcu
|
|
86
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
87
|
+
AND tc.table_schema = kcu.table_schema
|
|
88
|
+
WHERE tc.table_name = $1
|
|
89
|
+
AND tc.table_schema = 'public'
|
|
90
|
+
AND tc.constraint_type = 'PRIMARY KEY'
|
|
91
|
+
) pk ON pk.column_name = c.column_name
|
|
92
|
+
LEFT JOIN (
|
|
93
|
+
SELECT kcu.column_name, true AS is_unique
|
|
94
|
+
FROM information_schema.table_constraints tc
|
|
95
|
+
JOIN information_schema.key_column_usage kcu
|
|
96
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
97
|
+
AND tc.table_schema = kcu.table_schema
|
|
98
|
+
WHERE tc.table_name = $1
|
|
99
|
+
AND tc.table_schema = 'public'
|
|
100
|
+
AND tc.constraint_type = 'UNIQUE'
|
|
101
|
+
) uq ON uq.column_name = c.column_name
|
|
102
|
+
WHERE c.table_name = $1
|
|
103
|
+
AND c.table_schema = 'public'
|
|
104
|
+
ORDER BY c.ordinal_position
|
|
105
|
+
`, [tableName]);
|
|
106
|
+
return result.rows.map((r) => ({
|
|
107
|
+
name: r.column_name,
|
|
108
|
+
dataType: r.data_type,
|
|
109
|
+
udtName: r.udt_name,
|
|
110
|
+
isNullable: r.is_nullable === 'YES',
|
|
111
|
+
columnDefault: r.column_default,
|
|
112
|
+
isPrimaryKey: r.is_primary_key === true,
|
|
113
|
+
isUnique: r.is_unique === true,
|
|
114
|
+
characterMaxLength: r.character_maximum_length,
|
|
115
|
+
numericPrecision: r.numeric_precision,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
async function getForeignKeys(pool, tableName) {
|
|
119
|
+
const result = await pool.query(`
|
|
120
|
+
SELECT
|
|
121
|
+
tc.constraint_name,
|
|
122
|
+
kcu.column_name,
|
|
123
|
+
ccu.table_name AS referenced_table,
|
|
124
|
+
ccu.column_name AS referenced_column
|
|
125
|
+
FROM information_schema.table_constraints tc
|
|
126
|
+
JOIN information_schema.key_column_usage kcu
|
|
127
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
128
|
+
AND tc.table_schema = kcu.table_schema
|
|
129
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
130
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
131
|
+
AND ccu.table_schema = tc.table_schema
|
|
132
|
+
WHERE tc.table_name = $1
|
|
133
|
+
AND tc.table_schema = 'public'
|
|
134
|
+
AND tc.constraint_type = 'FOREIGN KEY'
|
|
135
|
+
`, [tableName]);
|
|
136
|
+
return result.rows.map((r) => ({
|
|
137
|
+
constraintName: r.constraint_name,
|
|
138
|
+
columnName: r.column_name,
|
|
139
|
+
referencedTable: r.referenced_table,
|
|
140
|
+
referencedColumn: r.referenced_column,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
async function getIndexes(pool, tableName) {
|
|
144
|
+
const result = await pool.query(`
|
|
145
|
+
SELECT
|
|
146
|
+
i.relname AS index_name,
|
|
147
|
+
array_agg(a.attname ORDER BY k.n) AS columns,
|
|
148
|
+
ix.indisunique AS is_unique,
|
|
149
|
+
ix.indisprimary AS is_primary
|
|
150
|
+
FROM pg_index ix
|
|
151
|
+
JOIN pg_class t ON t.oid = ix.indrelid
|
|
152
|
+
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
153
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
154
|
+
CROSS JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, n)
|
|
155
|
+
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
|
|
156
|
+
WHERE t.relname = $1
|
|
157
|
+
AND n.nspname = 'public'
|
|
158
|
+
GROUP BY i.relname, ix.indisunique, ix.indisprimary
|
|
159
|
+
`, [tableName]);
|
|
160
|
+
return result.rows.map((r) => ({
|
|
161
|
+
name: r.index_name,
|
|
162
|
+
columns: r.columns,
|
|
163
|
+
isUnique: r.is_unique,
|
|
164
|
+
isPrimary: r.is_primary,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
async function getRowCount(pool, tableName) {
|
|
168
|
+
const result = await pool.query(`SELECT reltuples::bigint AS estimate FROM pg_class WHERE relname = $1`, [tableName]);
|
|
169
|
+
const estimate = parseInt(result.rows[0]?.estimate || '0');
|
|
170
|
+
if (estimate < 10000) {
|
|
171
|
+
const exact = await pool.query(`SELECT count(*)::integer AS count FROM "${tableName}"`);
|
|
172
|
+
return parseInt(exact.rows[0]?.count || '0');
|
|
173
|
+
}
|
|
174
|
+
return Math.max(estimate, 0);
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=postgres.analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.analyzer.js","sourceRoot":"","sources":["../../../src/migration/analyzers/postgres.analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.exportConfig = exportConfig;
|
|
37
|
+
exports.importConfig = importConfig;
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const yaml = __importStar(require("js-yaml"));
|
|
40
|
+
function exportConfig(plan, filePath) {
|
|
41
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
42
|
+
const data = sanitizePlanForExport(plan);
|
|
43
|
+
if (ext === 'yaml' || ext === 'yml') {
|
|
44
|
+
(0, fs_1.writeFileSync)(filePath, yaml.dump(data, { indent: 2, lineWidth: 120 }));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
(0, fs_1.writeFileSync)(filePath, JSON.stringify(data, null, 2));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function importConfig(filePath) {
|
|
51
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
52
|
+
throw new Error(`Config file not found: ${filePath}`);
|
|
53
|
+
}
|
|
54
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
55
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
56
|
+
let data;
|
|
57
|
+
if (ext === 'yaml' || ext === 'yml') {
|
|
58
|
+
data = yaml.load(content);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
data = JSON.parse(content);
|
|
62
|
+
}
|
|
63
|
+
if (!data.version || !data.direction || !data.source || !data.target) {
|
|
64
|
+
throw new Error('Invalid migration config: missing required fields');
|
|
65
|
+
}
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
function sanitizePlanForExport(plan) {
|
|
69
|
+
return {
|
|
70
|
+
version: plan.version,
|
|
71
|
+
direction: plan.direction,
|
|
72
|
+
source: { type: plan.source.type, database: plan.source.database },
|
|
73
|
+
target: { type: plan.target.type, database: plan.target.database },
|
|
74
|
+
tableMappings: plan.tableMappings,
|
|
75
|
+
documentMappings: plan.documentMappings,
|
|
76
|
+
conflicts: plan.conflicts,
|
|
77
|
+
options: plan.options,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=config.manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.manager.js","sourceRoot":"","sources":["../../src/migration/config.manager.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,oCASC;AAED,oCAoBC;AAnCD,2BAA6D;AAC7D,8CAAgC;AAGhC,SAAgB,YAAY,CAAC,IAAmB,EAAE,QAAgB;IAChE,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAEzC,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QACpC,IAAA,kBAAa,EAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;SAAM,CAAC;QACN,IAAA,kBAAa,EAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IAErD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QACpC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,IAAqB,CAAC;AAC/B,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAmB;IAChD,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;QAClE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;QAClE,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { MigrationPlan, MigrationResult, MigrationOptions, AnalysisResult, ParsedDatabaseUrl } from '../types';
|
|
2
|
+
export declare function parseDatabaseUrl(url: string): ParsedDatabaseUrl;
|
|
3
|
+
export declare function analyzeDatabase(url: string): Promise<AnalysisResult>;
|
|
4
|
+
export declare function generateMigrationPlan(analysis: AnalysisResult, sourceUrl: string, targetUrl: string, options?: Partial<MigrationOptions>): MigrationPlan;
|
|
5
|
+
export declare function executeMigration(plan: MigrationPlan, onProgress?: (table: string, processed: number, total: number) => void): Promise<MigrationResult>;
|
|
6
|
+
export declare function maskUrl(url: string): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDatabaseUrl = parseDatabaseUrl;
|
|
4
|
+
exports.analyzeDatabase = analyzeDatabase;
|
|
5
|
+
exports.generateMigrationPlan = generateMigrationPlan;
|
|
6
|
+
exports.executeMigration = executeMigration;
|
|
7
|
+
exports.maskUrl = maskUrl;
|
|
8
|
+
const mongodb_analyzer_1 = require("../analyzers/mongodb.analyzer");
|
|
9
|
+
const postgres_analyzer_1 = require("../analyzers/postgres.analyzer");
|
|
10
|
+
const mongo_to_postgres_mapper_1 = require("../mappers/mongo-to-postgres.mapper");
|
|
11
|
+
const postgres_to_mongo_mapper_1 = require("../mappers/postgres-to-mongo.mapper");
|
|
12
|
+
const mongo_to_postgres_engine_1 = require("./mongo-to-postgres.engine");
|
|
13
|
+
const postgres_to_mongo_engine_1 = require("./postgres-to-mongo.engine");
|
|
14
|
+
function parseDatabaseUrl(url) {
|
|
15
|
+
if (url.startsWith('mongodb://') || url.startsWith('mongodb+srv://')) {
|
|
16
|
+
return (0, mongodb_analyzer_1.parseMongoUrl)(url);
|
|
17
|
+
}
|
|
18
|
+
if (url.startsWith('postgresql://') || url.startsWith('postgres://')) {
|
|
19
|
+
return (0, postgres_analyzer_1.parsePostgresUrl)(url);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Unsupported database URL. Expected "mongodb://", "mongodb+srv://", "postgresql://", or "postgres://"`);
|
|
22
|
+
}
|
|
23
|
+
async function analyzeDatabase(url) {
|
|
24
|
+
const parsed = parseDatabaseUrl(url);
|
|
25
|
+
if (parsed.type === 'mongodb') {
|
|
26
|
+
return (0, mongodb_analyzer_1.analyzeMongoDB)(url);
|
|
27
|
+
}
|
|
28
|
+
return (0, postgres_analyzer_1.analyzePostgres)(url);
|
|
29
|
+
}
|
|
30
|
+
function generateMigrationPlan(analysis, sourceUrl, targetUrl, options = {}) {
|
|
31
|
+
const targetParsed = parseDatabaseUrl(targetUrl);
|
|
32
|
+
if (analysis.type === 'mongodb' && targetParsed.type === 'postgresql') {
|
|
33
|
+
return (0, mongo_to_postgres_mapper_1.generateMongoToPostgresPlan)(analysis, sourceUrl, targetUrl, options);
|
|
34
|
+
}
|
|
35
|
+
if (analysis.type === 'postgresql' && targetParsed.type === 'mongodb') {
|
|
36
|
+
return (0, postgres_to_mongo_mapper_1.generatePostgresToMongoPlan)(analysis, sourceUrl, targetUrl, options);
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`Unsupported migration direction: ${analysis.type} → ${targetParsed.type}. ` +
|
|
39
|
+
`Supported: MongoDB → PostgreSQL, PostgreSQL → MongoDB`);
|
|
40
|
+
}
|
|
41
|
+
async function executeMigration(plan, onProgress) {
|
|
42
|
+
if (plan.direction === 'mongo_to_postgres') {
|
|
43
|
+
return (0, mongo_to_postgres_engine_1.executeMongoToPostgres)(plan, onProgress);
|
|
44
|
+
}
|
|
45
|
+
if (plan.direction === 'postgres_to_mongo') {
|
|
46
|
+
return (0, postgres_to_mongo_engine_1.executePostgresToMongo)(plan, onProgress);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Unsupported migration direction: ${plan.direction}`);
|
|
49
|
+
}
|
|
50
|
+
function maskUrl(url) {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = new URL(url);
|
|
53
|
+
if (parsed.password) {
|
|
54
|
+
parsed.password = '****';
|
|
55
|
+
}
|
|
56
|
+
return parsed.toString();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return url;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=migration.engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.engine.js","sourceRoot":"","sources":["../../../src/migration/engines/migration.engine.ts"],"names":[],"mappings":";;AAeA,4CAUC;AAED,0CAQC;AAED,sDAoBC;AAED,4CAaC;AAED,0BAUC;AA5ED,oEAA8E;AAC9E,sEAAmF;AACnF,kFAAkF;AAClF,kFAAkF;AAClF,yEAAoE;AACpE,yEAAoE;AAEpE,SAAgB,gBAAgB,CAAC,GAAW;IAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACrE,OAAO,IAAA,gCAAa,EAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACrE,OAAO,IAAA,oCAAgB,EAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAA,iCAAc,EAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,IAAA,mCAAe,EAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,qBAAqB,CACnC,QAAwB,EACxB,SAAiB,EACjB,SAAiB,EACjB,UAAqC,EAAE;IAEvC,MAAM,YAAY,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAEjD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACtE,OAAO,IAAA,sDAA2B,EAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,YAAY,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACtE,OAAO,IAAA,sDAA2B,EAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,KAAK,CACb,oCAAoC,QAAQ,CAAC,IAAI,MAAM,YAAY,CAAC,IAAI,IAAI;QAC5E,uDAAuD,CACxD,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,gBAAgB,CACpC,IAAmB,EACnB,UAAsE;IAEtE,IAAI,IAAI,CAAC,SAAS,KAAK,mBAAmB,EAAE,CAAC;QAC3C,OAAO,IAAA,iDAAsB,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,IAAI,CAAC,SAAS,KAAK,mBAAmB,EAAE,CAAC;QAC3C,OAAO,IAAA,iDAAsB,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,SAAgB,OAAO,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
|